解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
10.41 KiB
X
# SpanSerializer vs Binary 序列化性能测试 ## 性能概览 `SpanSerializer` 是基于 `SpanReader`/`SpanWriter` 的零流、无处理器链高性能序列化器,适用于 RPC 通信和文件读写。 测试结论:**SpanSerializer(反射路径) 比 Binary 快 9~21 倍;实现 `ISpanSerializable` 接口后可再快 2.5~4 倍,且零堆分配。** --- ## 测试环境 | 项目 | 值 | |------|-----| | CPU | Intel Core i9-10900K @ 3.70 GHz(20 逻辑核心)| | OS | Windows 10 22H2 (19045.6456) | | .NET | .NET 10.0 | | BenchmarkDotNet | v0.15.8 | | 编译模式 | Release | --- ## 测试方法 三条序列化路径: | 路径 | 说明 | |------|------| | **SpanSerializer(反射)** | 普通 POCO,通过编译委托(`Expression.Lambda`)序列化 | | **SpanSerializer(ISpanSerializable)** | 实现接口,零反射手写路径 | | **Binary** | 基于 `MemoryStream` + 处理器链的传统序列化 | 测试模型: - `SimpleModel`:5 个字段(Int32/String/Boolean/DateTime/Double) - `NestedModel`:4 个字段 + 内嵌 SimpleModel - `FastModel`:同 SimpleModel,但实现 `ISpanSerializable` 测试维度:单次操作(序列化/反序列化)、批量操作(100 / 1000 条)、多线程并发(1 / 4 / 8 / 20 / 32 线程)。 --- ## 测试结果 ### 1. 单次序列化 > 基准(Baseline)= `Span_Simple序列化_到Span` | 方法 | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | |------|-----:|------:|-------:|------:|-----:|----------:|------------:| | Span_Simple序列化_到Span *(Baseline)* | 59.03 ns | 0.432 ns | 0.067 ns | 1.00 | 0.0092 | 96 B | 1.00 | | Span_Simple序列化_池化包 | 64.80 ns | 1.473 ns | 0.382 ns | 1.10 | 0.0092 | 96 B | 1.00 | | Span_Nested序列化_到Span | 110.56 ns | 1.166 ns | 0.303 ns | 1.87 | 0.0137 | 144 B | 1.50 | | **Span_Fast序列化_到Span** | **23.49 ns** | 0.113 ns | 0.029 ns | **0.40** | - | **0 B** | **0.00** | | Span_Fast序列化_池化包 | 29.26 ns | 0.059 ns | 0.015 ns | 0.50 | - | 0 B | 0.00 | | Binary_Simple序列化 | 800.83 ns | 15.063 ns | 3.912 ns | 13.57 | 0.1926 | 2016 B | 21.00 | | Binary_Nested序列化 | 1,222.42 ns | 19.373 ns | 2.998 ns | 20.71 | 0.2632 | 2752 B | 28.67 | ### 2. 单次反序列化 > 基准(Baseline)= `Span_Simple反序列化` | 方法 | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | |------|-----:|------:|-------:|------:|-----:|----------:|------------:| | Span_Simple反序列化 *(Baseline)* | 111.96 ns | 1.599 ns | 0.415 ns | 1.00 | 0.0175 | 184 B | 1.00 | | Span_Nested反序列化 | 219.83 ns | 2.998 ns | 0.464 ns | 1.96 | 0.0312 | 328 B | 1.78 | | **Span_Fast反序列化** | **75.72 ns** | 0.726 ns | 0.189 ns | **0.68** | 0.0083 | **88 B** | **0.48** | | Binary_Simple反序列化 | 1,008.17 ns | 14.916 ns | 3.874 ns | 9.00 | 0.2270 | 2392 B | 13.00 | | Binary_Nested反序列化 | 1,747.46 ns | 10.144 ns | 2.634 ns | 15.61 | 0.3204 | 3360 B | 18.26 | ### 3. 批量操作(100 / 1000 条 SimpleModel) > 基准(Baseline)= `Span_批量序列化`(同 Count 分组) | Count | 方法 | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | |------:|------|-----:|------:|-------:|------:|-----:|----------:|------------:| | 100 | Span_批量序列化 *(Baseline)* | 5.497 μs | 0.100 μs | 0.016 μs | 1.00 | 0.9155 | 9.38 KB | 1.00 | | 100 | Span_批量反序列化 | 10.695 μs | 0.191 μs | 0.050 μs | 1.95 | 1.7548 | 17.97 KB | 1.92 | | 100 | Binary_批量序列化 | 55.637 μs | 0.552 μs | 0.143 μs | 10.12 | 9.2773 | 94.89 KB | 10.12 | | 100 | Binary_批量反序列化 | 76.196 μs | 0.806 μs | 0.209 μs | 13.86 | 10.254 | 105.83 KB | 11.29 | | 1000 | Span_批量序列化 *(Baseline)* | 55.577 μs | 0.681 μs | 0.177 μs | 1.00 | 9.1553 | 93.75 KB | 1.00 | | 1000 | Span_批量反序列化 | 109.082 μs | 0.975 μs | 0.253 μs | 1.96 | 17.578 | 179.69 KB | 1.92 | | 1000 | Binary_批量序列化 | 576.703 μs | 15.012 μs | 3.899 μs | 10.38 | 91.797 | 938.71 KB | 10.01 | | 1000 | Binary_批量反序列化 | 783.185 μs | 35.201 μs | 9.142 μs | 14.09 | 102.54 | 1048.09 KB | 11.18 | ### 4. 多线程并发(SimpleModel 序列化/反序列化) > 基准(Baseline)= `Span_并发序列化`(同 ThreadCount 分组) | ThreadCount | 方法 | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio | |------------:|------|-----:|------:|-------:|------:|-----:|----------:|------------:| | 1 | Span_并发序列化 *(Baseline)* | 2.621 μs | 0.043 μs | 0.011 μs | 1.00 | 0.1564 | 1.63 KB | 1.00 | | 1 | Span_并发反序列化 | 2.624 μs | 0.030 μs | 0.008 μs | 1.00 | 0.1678 | 1.71 KB | 1.05 | | 1 | Binary_并发序列化 | 2.660 μs | 0.023 μs | 0.004 μs | 1.01 | 0.3738 | 3.84 KB | 2.36 | | 1 | Binary_并发反序列化 | 2.617 μs | 0.090 μs | 0.023 μs | 1.00 | 0.3662 | 3.88 KB | 2.38 | | 4 | Span_并发序列化 *(Baseline)* | 2.584 μs | 0.039 μs | 0.010 μs | 1.00 | 0.1984 | 2.07 KB | 1.00 | | 4 | Span_并发反序列化 | 2.635 μs | 0.021 μs | 0.006 μs | 1.02 | 0.2365 | 2.42 KB | 1.17 | | 4 | Binary_并发序列化 | 4.896 μs | 0.096 μs | 0.025 μs | 1.89 | 1.0986 | 11.23 KB | 5.42 | | 4 | Binary_并发反序列化 | 5.681 μs | 0.100 μs | 0.026 μs | 2.20 | 1.0986 | 11.36 KB | 5.48 | | 8 | Span_并发序列化 *(Baseline)* | 2.667 μs | 0.110 μs | 0.028 μs | 1.00 | 0.2594 | 2.67 KB | 1.00 | | 8 | Span_并发反序列化 | 2.768 μs | 0.076 μs | 0.020 μs | 1.04 | 0.3204 | 3.36 KB | 1.26 | | 8 | Binary_并发序列化 | 7.096 μs | 0.241 μs | 0.063 μs | 2.66 | 2.0447 | 20.93 KB | 7.82 | | 8 | Binary_并发反序列化 | 8.651 μs | 0.188 μs | 0.049 μs | 3.24 | 2.0752 | 21.23 KB | 7.94 | | 20 | Span_并发序列化 *(Baseline)* | 3.726 μs | 0.098 μs | 0.026 μs | 1.00 | 0.4425 | 4.59 KB | 1.00 | | 20 | Span_并发反序列化 | 4.750 μs | 0.121 μs | 0.031 μs | 1.27 | 0.6104 | 6.32 KB | 1.37 | | 20 | Binary_并发序列化 | 12.809 μs | 0.265 μs | 0.069 μs | 3.44 | 4.9133 | 49.94 KB | 10.87 | | 20 | Binary_并发反序列化 | 13.206 μs | 0.919 μs | 0.239 μs | 3.54 | 4.5929 | 46.67 KB | 10.16 | | 32 | Span_并发序列化 *(Baseline)* | 5.248 μs | 0.050 μs | 0.013 μs | 1.00 | 0.6256 | 6.40 KB | 1.00 | | 32 | Span_并发反序列化 | 6.777 μs | 0.209 μs | 0.054 μs | 1.29 | 0.8850 | 9.28 KB | 1.45 | | 32 | Binary_并发序列化 | 16.732 μs | 0.138 μs | 0.021 μs | 3.19 | 7.7515 | 78.91 KB | 12.32 | | 32 | Binary_并发反序列化 | 18.248 μs | 0.301 μs | 0.078 μs | 3.48 | 7.8735 | 79.96 KB | 12.49 | --- ## 核心指标 > 以下吞吐量基于单线程单次操作均值换算(ops/ms = 1,000,000 ns ÷ Mean ns) | 操作 | SpanSerializer(反射) | SpanSerializer(ISpanSerializable) | Binary | |------|---------------------:|----------------------------------:|-------:| | 序列化 SimpleModel | ~16,940,000 ops/s | ~42,570,000 ops/s | ~1,249,000 ops/s | | 反序列化 SimpleModel | ~8,932,000 ops/s | ~13,206,000 ops/s | ~992,000 ops/s | | 批量序列化(1000条)| ~17,994,000 ops/s | — | ~1,735,000 ops/s | | 批量反序列化(1000条)| ~9,167,000 ops/s | — | ~1,277,000 ops/s | --- ## 对比分析 ### 纵向:多线程并发趋势 `SpanSerializer` 并发序列化耗时从 1 线程的 2.62 μs 增长到 32 线程的 5.25 μs,增幅约 **100%**,说明 `Parallel.For` 和线程调度本身带来了固定开销,但实际吞吐随线程增加线性扩展。 `Binary` 并发序列化从 1 线程 2.66 μs 增长到 32 线程 16.73 μs,增幅约 **529%**,主要原因是每次调用需创建 `MemoryStream`,高并发下 GC 压力显著放大(32 线程内存分配达 78.91 KB/op,是 SpanSerializer 的 12.3 倍)。 | ThreadCount | SpanSerializer序列化 | Binary序列化 | 差距倍数 | |------------:|--------------------:|------------:|--------:| | 1 | 2.62 μs | 2.66 μs | ≈1.0× | | 4 | 2.58 μs | 4.90 μs | 1.9× | | 8 | 2.67 μs | 7.10 μs | 2.7× | | 20 | 3.73 μs | 12.81 μs | 3.4× | | 32 | 5.25 μs | 16.73 μs | 3.2× | > 单线程下两者耗时接近(Parallel.For 本身有固定开销),**4 线程以上 SpanSerializer 优势持续扩大**。 ### 横向:各场景速度与内存对比 | 场景 | SpanSerializer(反射) 速度优势 | SpanSerializer 内存节省 | |------|-----------------------------:|------------------------:| | 单次序列化 Simple | **13.6×** 快 | 95.2%(96 B vs 2016 B)| | 单次序列化 Nested | **11.1×** 快 | 94.8%(144 B vs 2752 B)| | 单次反序列化 Simple | **9.0×** 快 | 92.3%(184 B vs 2392 B)| | 单次反序列化 Nested | **7.9×** 快 | 90.2%(328 B vs 3360 B)| | 批量序列化 1000 条 | **10.4×** 快 | 90.0%(93.75 KB vs 938.71 KB)| | 批量反序列化 1000 条 | **7.2×** 快 | 82.8%(179.69 KB vs 1048.09 KB)| | ISpanSerializable 序列化 | **34.1×** 快 | 100%(0 B vs 2016 B)| | ISpanSerializable 反序列化 | **13.3×** 快 | 96.3%(88 B vs 2392 B)| --- ## 性能瓶颈定位 ### SpanSerializer 反射路径 - **主要开销**:`GetSchema` → `Getter/Setter` 委托调用(已缓存编译,无热点反射) - **次要开销**:装箱(`Object?` 传递值类型)——序列化每属性一次 box/unbox - 反序列化比序列化慢约 **1.9×**,因为还需 `CreateInstance` 和 `Setter` 写回 ### Binary - **主要开销**:每次操作创建 `MemoryStream`(`new MemoryStream(256)` ≈ 200 ns 分配) - **次要开销**:处理器链逐 handler 匹配(`foreach Handlers`),每字段至少走 2~3 个 handler - 高并发下 GC Gen0 压力是 SpanSerializer 的 10~12 倍 ### ISpanSerializable 路径 - 零反射,无装箱(直接 `writer.Write(Id)` 写值类型) - 序列化仅 **23.49 ns**,无任何堆分配,适用于极低延迟场景 - 代价是需要手写 `Write`/`Read` 方法,维护成本较高 --- ## 优化建议 | 优先级 | 建议 | 预期收益 | |--------|------|---------| | ★★★ | 频繁序列化的核心数据结构实现 `ISpanSerializable`,获得零反射+零分配 | 序列化再快 2.5×,内存降至 0 | | ★★☆ | SpanSerializer 内部用泛型重写 `WriteValue`/`ReadValue`,消除值类型装箱 | 反射路径再提速 20~40% | | ★★☆ | 反序列化路径缓存 `CreateInstance` 的默认构造函数委托,避免每次反射 | 反序列化提速 10~20% | | ★☆☆ | Binary 场景若仍需使用,复用 `MemoryStream`(改用 `ArrayPool` + 重置 Position)| 高并发内存降低 80% | | ★☆☆ | 考虑支持 `Span<T>` 列表/数组属性的自动序列化,减少 `ISpanSerializable` 手写量 | 扩大适用范围 |