解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# 裸 Socket 层性能测试报告
> 测试范围:**不含协议编码器**的裸 Socket 收发链路——TCP/UDP × 单包单帧/单包多帧(粘包)× 包大小(32B~1MB)× 并发 × 客户端接收方式;**吞吐与分配以服务端为口径(优化目标),往返延迟为客户端观察**。
> 关联报告:[网络库编解码器Echo性能测试报告](/NewLife/X/Blob/v12.0.2026.0927/Doc/性能/网络库编解码器Echo性能测试报告.md)(协议层)、[内存分配与拷贝成本报告](/NewLife/X/Blob/v12.0.2026.0927/Doc/性能/../内存分配与拷贝成本报告.md)。
> 数据版本:**v3.1(2026-09-23 优化后复测)**——服务端口径(吞吐/分配)、并发与尺寸扫描、带宽比特率单位;UDP 服务端分配 330→216 B/msg、客户端连接化,UDP 高并发吞吐 +7~25%(见第七节);工具 `Benchmark/NetLoadTest`(`run-matrix.ps1` 一键复现)。
## 一、核心结论
> **口径**:服务端与客户端分离进程;**吞吐与分配 = 服务端口径**(吞吐取服务端接收稳态;分配取服务端进程每消息托管分配),**延迟 = 客户端观察 RTT**(往返时间只能由发起方测);服务端接收始终是异步接收环(SAEA)。单位:带宽 = 比特率(1 Gbps = 10⁹ bit/s),包率 = kpps / Mpps。
### 表 1 吞吐(服务端稳态,各档取最优并发)
| 协议 | 场景 | 最优并发 | 带宽 | 包率 | 帧率(粘包) | 服务端分配 B/msg |
|---|---|---:|---:|---:|---:|---:|
| TCP | 单帧 32B | 8C | 0.38 Gbps | 1.49 Mpps | — | 0.08 |
| TCP | 单帧 1KB | 32C | **13.7 Gbps** | 1.67 Mpps | — | 0.2 |
| TCP | 单帧 64KB | 4C * | **53.8 Gbps** | 103 kpps | — | 1.0 |
| TCP | 单帧 1MB | 8C | 33.7 Gbps | 4.0 kpps | — | 252 |
| TCP | 粘包 24B 帧(64KB 包) | 8C | 42.2 Gbps | — | **218 M帧/s** | 1.5 |
| UDP | 单帧 32B | 4C | 0.06 Gbps | 227 kpps | — | 216 |
| UDP | 单帧 256B | 4C | 0.59 Gbps | 288 kpps | — | 216 |
| UDP | 单帧 1KB | 64C | 4.08 Gbps | **499 kpps** | — | 216 |
| UDP | 单帧 4KB | 4C | 8.9 Gbps | 272 kpps | — | 216 |
| UDP | 单帧 16KB | 8C | 38.8 Gbps | 296 kpps | — | 216 |
| UDP | 单帧 64KB(IP 分片) | 32C | **134.5 Gbps** | 257 kpps | — | 216 |
| UDP | 粘包 24B 帧(64KB 包) | 4C | 82.1 Gbps | — | **422 M帧/s** | 216 |
> * TCP 大包样本跨运行波动大:64KB 4 并发复测 6 次为 49.0/53.8/54.2/53-57(其中一次 40);8 并发复测 5 次为 29.2/40.6/49.9/41.4/41.5(中位 40.6)。
### 表 2 往返延迟(客户端观察,µs)
| 协议 | 包大小 | 并发 | 接收方式 | P50 | P95 | P99 | 服务端分配 B/msg |
|---|---|---:|---|---:|---:|---:|---:|
| TCP | 32B | 4 | 同步 | 49.8 | 69.5 | 81 | 1.1 |
| TCP | 1KB | 4 | 同步 / 异步 / 事件 | 48.2 / 56.6 / 52.8 | 65.2 / 75.8 / 69.6 | 74 / 88 / 81 | 1.1-1.3 |
| TCP | 64KB | 4 | 同步 / 异步 / 事件 | 100.0 / 96.1 / 86.7 | 110.8 / 116.3 / 105.0 | 135 / 129 / 118 | 1.9-2.2 |
| TCP | 1MB | 4 | 同步 | 1408.6 | 1843.7 | 2202 | 208 |
| TCP | 1KB | 64 | 同步 / 异步 / 事件 | 275.1 / 276.9 / 266.6 | 401.5 / 396.3 / 390.0 | 456 / 467 / 431 | 2.4-2.5 |
| TCP | 64KB | 64 | 同步 / 异步 / 事件 | 1499.6 / 1336.1 / 1414.2 | 2682.4 / 2670.0 / 2671.3 | 3358 / 3309 / 3270 | 11.7-12.6 |
| UDP | 32B | 1 | 同步 | 32.6 | 49.2 | 87 | 258 |
| UDP | 1KB | 1 | 同步 / 事件 | 40.7 / 48.7 | 49.6 / 59.7 | 85 / 94 | 258-260 |
| UDP | 64KB | 1 | 同步 | 59.8 | 75.6 | 130 | 260 |
### 浓缩结论
- **TCP 小包由每包固定成本主导**:32B 与 1KB 包率同档(≈1.5 Mpps);1KB 带宽从 1 并发 0.58 Gbps 升至 32 并发 **13.7 Gbps**,64 并发回落(12.7)——拐点在 16-32 并发。
- **TCP 大包进每字节成本区间**:64KB 4 并发 **53.8 Gbps(≈6.4 GiB/s)**、8 并发中位 40.6;1MB 档 33.7 Gbps。
- **UDP 包率与并发强相关**:1KB 从 4C 的 284 kpps 升至 16-64C 的 **494-499 kpps**(优化后 +20%);64KB 从 4C 82.5 Gbps 升至 32C 的 **134.5 Gbps**。
- **UDP 服务端每消息分配恒定 216 B(TCP 近零)**:v3.1 已把每包字符串键查找开销消除(330→216,GC 频率 ~-40%,见第七节);剩余为运行时端点对象开销(SAEA 完成回调就地更新并随会话保留,每包必须新建 IPEndPoint)。
- **延迟**:TCP 1KB 4C P50 ≈48µs、64C ≈267-277µs;事件接收尾延迟最好(64C P99 431µs vs 同步 456µs);UDP P50 33-41µs;1MB 大包往返 P50 ≈1.4ms。
- **分配**:TCP 服务端 0.08-4.8 B/msg(GC 全零,除 1MB 档 252 B/msg 待查);UDP 服务端 216 B/msg(每 ~6.3 万包一次 Gen0);UDP 客户端发送 41.5→**0.4 B/msg**(v3.1 自动连接,见第七节)。
## 二、测试方法
### 测试环境
```text
Windows 10 (10.0.19045.6456/22H2)
Intel Core i9-10900K CPU 3.70GHz, 1 CPU, 20 逻辑核心 / 10 物理核心
.NET SDK 10.0.401
Runtime: .NET 10.0.12, X64 RyuJIT x86-64-v3(Server GC)
网络:loopback(127.0.0.1),服务端与客户端分离进程
```
### 工具与口径(`Benchmark/NetLoadTest`)
- 一键复现:`Benchmark/NetLoadTest/run-matrix.ps1`(全矩阵约 20 分钟)输出 `results.md / results.json`;单场景直连 `NetLoadTest.exe`;
- **分离进程**:服务端独立进程(`--server`,静默 3 秒输出 `STEADY` 稳态中位数:带宽/包率/帧率/服务端分配/GC,然后自动退出),客户端 `--remote` 连接;
- **三种测量模式**:`--oneway` 单向上行(服务端只计数,测纯接收)/`pipeline` 回显流水线/`roundtrip` 逐包往返(采样 P50/P95/P99);
- **客户端接收方式** `--recvmode`:`sync` 同步拉取/`asyncpull` 异步拉取(`ReceiveAsync`)/`event` 事件接收(`Received` 回调);
- 口径要素:预热 2s + 窗口 10s;测量前发**预热流量**(触发服务端冷启动 JIT 与池化初始化,避免首样本计入百毫秒级冷启动);服务端接收始终是异步接收环(SAEA);`--frame 24` 模拟粘包(24B 逻辑帧);UDP 单包钳制 65507 字节;带宽用比特率(1 Gbps = 10⁹ bit/s ≈ 119 MiB/s)。
## 三、吞吐(服务端接收稳态)
### 3.1 TCP
**尺寸曲线(8 客户端)**
| 包大小 | 带宽 | 包率 | 服务端分配 B/msg | GC |
|---:|---:|---:|---:|---:|
| 32B | 0.38 Gbps | 1.49 Mpps | 0.08 | 0/0/0 |
| 1KB | 12.1 Gbps * | 1.48 Mpps | 0.1 | 0/0/0 |
| 64KB | 40.6 Gbps * | 77 kpps | 2.2 | 0/0/0 |
| 1MB | 33.7 Gbps | 4.0 kpps | 252 | 1/1/1 |
**并发扫描(1KB 与 64KB)**
| 并发 | 1KB 带宽 / 包率 | 64KB 带宽 |
|---:|---:|---:|
| 1 | 0.58 Gbps / 70 kpps | 17.1 Gbps |
| 4 | 6.71 Gbps / 819 kpps | **53.8 Gbps**(复测 49.0/54.2) |
| 8 | 12.1 Gbps * / 1.48 Mpps | 40.6 Gbps *(复测 5 次 29-50) |
| 16 | 13.6 Gbps / 1.66 Mpps | 38.6 Gbps |
| 32 | **13.7 Gbps** / 1.67 Mpps | 33.4 Gbps |
| 64 | 12.7 Gbps / 1.55 Mpps | — |
> * 8 并发两档跨运行波动较大,取当日复测中位(1KB:9.7/12.2/12.1;64KB:29.2/40.6/49.9)。
- 小包拐点在 **16-32 并发**(≈13.7 Gbps),再增加不再提升——单进程接收环饱和;
- 64KB 大包 4 并发即达峰(≈54 Gbps、6.4 GiB/s),更多并发反而回落;
- 服务端分配除 1MB 档外全部 ≈0(GC 零次):接收路径「轮末裁决 + 句柄回挂」已达近零分配。
### 3.2 UDP
**尺寸曲线(4 客户端)**
| 包大小 | 带宽 | 包率 | 服务端分配 B/msg |
|---:|---:|---:|---:|
| 32B | 0.06 Gbps | 227 kpps | 216 |
| 256B | 0.59 Gbps | 288 kpps | 216 |
| 1KB | 2.33 Gbps | 284 kpps | 216 |
| 4KB | 8.9 Gbps | 272 kpps | 216 |
| 16KB | 30.8 Gbps | 235 kpps | 216 |
| 64KB(IP 分片) | 82.5 Gbps | 158 kpps | 216 |
**并发扫描(1KB 与 64KB)**
| 并发 | 1KB 带宽 / 包率 | 64KB 带宽 |
|---:|---:|---:|
| 1 | 0.77 Gbps / 94 kpps | 26.2 Gbps |
| 2 | 1.39 Gbps / 170 kpps | 50.0 Gbps |
| 4 | 2.33 Gbps / 284 kpps | 82.5 Gbps |
| 8 | 3.01 Gbps / 368 kpps | 109.8 Gbps(复测 122.1/115.3) |
| 16 | 4.05 Gbps / **494 kpps**(复测 4.14G/506kpps) | 126.9 Gbps |
| 32 | 3.98 Gbps / 486 kpps | **134.5 Gbps** |
| 64 | 4.08 Gbps / **499 kpps** | — |
**客户端连接化**(v3.1 库内置:纯客户端模式打开即 Connect 远端)
| 指标 | 旧版(SendTo) | v3.1(自动连接) |
|---|---:|---:|
| 服务端吞吐(1KB 4C / 64KB 4C) | 2.34 / 81.2 Gbps | 2.36 / 82.5 Gbps(不变) |
| 客户端分配 | 41.5 B/msg | **0.4 B/msg** |
- 未连接 UDP 发送走 `Socket.SendTo`,每包序列化 `SocketAddress`(实测 43 B/包);连接化后走 `Send`(零分配)——收益在客户端,服务端吞吐不变;
- 优化后 UDP 服务端分配恒定 **216 B/msg**(详见第七节):包率从 4C 284 kpps 提升至 16-64C 的 494-506 kpps,1KB 高并发吞吐较优化前 +20%。
### 3.3 粘包多帧(24B 逻辑帧)
| 协议 | 对齐后大包 | 帧/包 | 包率 | 帧率 | 带宽 |
|---|---:|---:|---:|---:|---:|
| TCP | 1,008 B | 42 | 1.47 Mpps | 60.9 M帧/s | 11.9 Gbps |
| TCP | 65,520 B | 2,730 | 80.6 kpps | **218 M帧/s** | 42.2 Gbps |
| UDP | 1,008 B | 42 | 287 kpps | 11.8 M帧/s | 2.32 Gbps |
| UDP | 65,496 B | 2,729 | 157 kpps | **422 M帧/s** | 82.1 Gbps |
- 帧率随大包尺寸大幅提升(固定成本被 2,700+ 帧摊薄);业务侧批量化是性价比最高的吞吐手段。
### 3.4 回显流水线(客户端接收方式对照)
| 包大小 | 接收方式 | 回显吞吐 | 带宽 | 服务端分配 | 客户端分配 |
|---:|---|---:|---:|---:|---:|
| 1KB | 同步 / 异步 / 事件 | 1.05 / 1.23 / **1.27** M msg/s | 8.6 / 10.1 / 10.4 Gbps | 0.09-0.15 | 4.4 / 24.3 / **0.01** |
| 64KB | 同步 / 异步 / 事件 | 21.2 / **25.2** / 22.9 k msg/s | 11.1 / 13.2 / 12.0 Gbps | 12.0-13.6 | 156 / 568 / **0.8** |
- 1KB 档事件接收吞吐最高(1.27 M msg/s)且近零分配;**客户端分配差异显著**:异步拉取状态机最高(1KB 24 B/msg、64KB 568 B/msg),事件接收近零(见第五节)。
## 四、往返延迟(客户端观察 RTT)
> RTT 只能由发起方(客户端)观察;下列延迟含客户端路径成本,服务端处理成本见「服务端分配」列。
### 4.1 TCP
| 包大小 | 并发 | 接收方式 | P50 | P95 | P99 | max | 服务端分配 B/msg |
|---:|---:|---|---:|---:|---:|---:|---:|
| 32B | 4 | 同步 | 49.8 | 69.5 | 80.8 | 4,433 | 1.1 |
| 1KB | 4 | 同步 | 48.2 | 65.2 | 74.3 | 4,853 | 1.1 |
| 64KB | 4 | 同步 | 100.0 | 110.8 | 135.3 | 3,250 | 2.1 |
| 1MB | 4 | 同步 | 1,408.6 | 1,843.7 | 2,201.6 | 4,784 | 208 |
| 1KB | 64 | 同步 | 275.1 | 401.5 | 455.7 | 16,361 | 2.5 |
| 64KB | 64 | 同步 | 1,499.6 | 2,682.4 | 3,358.2 | 6,877 | 12.6 |
| 1KB | 4 | 异步 | 56.6 | 75.8 | 87.9 | 1,570 | 1.3 |
| 1KB | 64 | 异步 | 276.9 | 396.3 | 467.3 | 47,725 | 2.5 |
| 64KB | 4 | 异步 | 96.1 | 116.3 | 129.1 | 1,645 | 2.1 |
| 64KB | 64 | 异步 | 1,336.1 | 2,670.0 | 3,308.5 | 11,087 | 11.7 |
| 1KB | 4 | 事件 | 52.8 | 69.6 | 81.1 | 2,528 | 1.1 |
| 1KB | 64 | 事件 | 266.6 | 390.0 | 430.5 | 22,824 | 2.4 |
| 64KB | 4 | 事件 | 86.7 | 105.0 | 117.6 | 1,135 | 1.9 |
| 64KB | 64 | 事件 | 1,414.2 | 2,671.3 | 3,270.1 | 8,766 | 12.3 |
- 包大小:32B/1KB 同档(P50 ≈48-50µs,链路固定成本);64KB→87-100µs、1MB→1.4ms(带宽支配);
- 并发:4→64 使小包 P50 升至 267-277µs;64KB 大包 64 并发升至 1.34-1.50ms;
- 接收方式:低并发小包同步与事件接近(48.2 vs 52.8µs);事件接收在 64KB 与尾延迟上最优(64C P99 431µs vs 同步 456µs、异步 467µs)。
### 4.2 UDP
| 包大小 | 并发 | 接收方式 | P50 | P95 | P99 | max | 服务端分配 B/msg |
|---:|---:|---|---:|---:|---:|---:|---:|
| 32B | 1 | 同步 | 32.6 | 49.2 | 87.0 | 5,824 | 258 |
| 1KB | 1 | 同步 | 40.7 | 49.6 | 84.9 | 5,882 | 258 |
| 64KB | 1 | 同步 | 59.8 | 75.6 | 129.8 | 6,104 | 260 |
| 1KB | 1 | 事件 | 48.7 | 59.7 | 94.1 | 2,236 | 260 |
- UDP 单次往返 P50 ≈33-41µs,快于 TCP(省去连接/确认状态机开销);64KB 单包 P50 60µs(IP 分片);max 为偶发调度毛刺,低频可忽略;服务端分配 258-260 B/msg(接收+回显,较优化前 373 降 30%)。
**延迟构成**:低并发最小往返 ≈23-27µs(TCP);该数值不是 loopback 物理极限(内核单次传输 1-1.2µs),大头是**两次用户态系统调用(Send + 阻塞 Receive 唤醒)+ 线程调度 + 服务端回显链路**。
## 五、内存分配(服务端口径)
| 场景 | 服务端 B/msg | GC 0/1/2 | 说明 |
|---|---:|---|---|
| TCP 单帧 32B~64KB | 0.08-2.5 | 0/0/0 | 接收路径近零(轮末裁决 + 句柄回挂复用) |
| TCP 单帧 1MB | 252 | 1/1/1 | 大缓冲池行为,待专项核实 |
| TCP 回显(32B~64KB) | 0.09-13.6 | 0/0/0 | 接收 + 回显发送 |
| UDP 单帧(全尺寸) | **216** | 每 ~6.3 万包一次 Gen0 | v3.1 已消除每包字符串键查找(原 330);剩余为运行时端点对象开销 |
| UDP 往返(接收 + 回显) | 258-260 | — | 较优化前 373 降 30% |
| TCP 客户端同步拉取往返 | 200 | — | 每包 `OwnerPacket + ArrayOwner` 句柄对 |
| TCP 客户端异步拉取往返 | 615-657 | — | 每 await 的 async 状态机 + Task |
| TCP 客户端事件往返 | 41-63 | — | 接收环复用句柄,近零 |
| TCP 客户端流水线(同步/异步/事件) | 4.4 / 24.3 / **0.01** | — | 1KB;事件模式零分配 |
| UDP 客户端发送(旧版 SendTo / v3.1) | 41.5 / **0.4** | — | v3.1 自动连接后走 `Send` |
**优化进展(v3.1)**
1. ✅ **UDP 服务端接收路径**(已落地):会话查找由字符串键改为**端点键缓存**——330→216 B/msg(-35%),Gen0 频率 ~-40%,UDP 高并发吞吐 +7~25%(见第七节);
2. ✅ **UDP 客户端 Connect 化**(已落地):库内置纯客户端模式自动连接,41.5→0.4 B/msg;
3. ⏳ TCP 1MB 档 252 B/msg 与 GC 1/1/1 待专项核实(疑与大缓冲池复用策略相关)。
## 六、已知限制
- **UDP 无流控**:发送端超过服务端消费能力必然丢包(灌包过载场景),业务需自行限速;调大 `UdpServer.MaxAsync` 可增大接收环并行度(实测对包率上限无提升,1KB 极限约 500 kpps);
- **UDP 服务端仍有恒定 216 B/msg**(原 330):为 .NET SAEA 完成回调的端点对象开销——运行时就地更新 `RemoteEndPoint` 且对象随会话保留,每轮接收必须新建 `IPEndPoint`,应用层无法安全规避(复用于重置会改到其它会话的端点);
- **TCP 1MB 档 252 B/msg**:待专项核实(大缓冲池复用策略);
- **`UdpSession` 不实现 `INetSession`**:`NetServer.Received` 的 sender 在 UDP 下为 `UdpSession`(仅 `ISocketSession`),`s is INetSession` 分支不命中,写通用代码时注意。
## 七、优化记录(v3.1,2026-09-23 复测)
| 优化项 | 实现 | 效果(复测) |
|---|---|---|
| UDP 会话查找去字符串键 | `SessionCollection` 新增端点键缓存(`ConcurrentDictionary<IPEndPoint>`;未命中回退字符串键,创建/移除/清理各路径同步维护并做值校验) | 服务端分配 **330→216 B/msg(-35%)**,Gen0 频率 ~-40%;UDP 高并发吞吐 +7~25%(1KB 16C 404→494 kpps、64C 499 kpps;64KB 8C 94→110-122 Gbps、32C 126→134.5 Gbps) |
| UDP 客户端自动连接 | 纯客户端模式(本地端口自动分配 + 远端为单播地址)打开即 `Connect` 远端;广播/组播/指定本地端口的双角色实例保持 SendTo 语义 | 客户端发送 **41.5→0.4 B/msg**;服务端吞吐不变(2.34→2.36 Gbps 同档) |
| 接收环并行度实验 | `MaxAsync` ×4(51 槽位)对照实验 | 包率无变化 → 槽位数不是 UDP 包率上限的瓶颈;保持默认 CPU×1.6 |
**剩余 216 B/msg 的成因**:经 .NET 运行时源码确认,SAEA 完成回调会**就地更新** `RemoteEndPoint` 对象(`EndPoint.Create` 语义),且该对象随接收结果成为会话的远程端点被长期保留——每轮接收必须使用新的 `IPEndPoint`;复用同一实例重置会把其它会话的端点改掉。该开销在应用层已无安全优化空间。
**验证**:新增单元测试 `UdpServerCreateSessionEndPointCache`(同端点复用/销毁后重建)、`UdpClientAutoConnectRemote`(自动连接+收发)、`UdpServerNoAutoConnectForBroadcastAndLocalPort`(广播/双角色不连接);UDP 相关 32 用例全绿;TCP 路径不受影响(复测同档)。
## 复现命令
```bash
# 全矩阵一键跑批(约 20 分钟;-Quick 冒烟 / -SkipBuild 跳过编译 / -Only 正则过滤)
pwsh -File Benchmark/NetLoadTest/run-matrix.ps1
# 单场景:分离进程单向吞吐(服务端独立进程 + 客户端只发不收;服务端 STEADY 含分配/GC)
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --server --port 7789 --oneway
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --remote 127.0.0.1:7789 --clients 8 --size 65536 --seconds 10 --warmup 2 --oneway
# 单场景:往返延迟(客户端接收方式可选 sync|asyncpull|event)
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --server --port 7789 --size 1024
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --remote 127.0.0.1:7789 --mode roundtrip --clients 4 --size 1024 --seconds 10 --recvmode event
# 粘包口径(24B 逻辑帧)与 UDP 连接化实验
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --remote 127.0.0.1:7789 --clients 8 --size 65536 --seconds 10 --oneway --frame 24
Benchmark/NetLoadTest/bin/Release/net10.0/NetLoadTest.exe --remote 127.0.0.1:7789 --udp --clients 4 --size 1024 --seconds 10 --oneway --udpconnect
```
|