性能概览

NewLife.Remoting 是基于 TCP 的高性能 RPC 框架,提供 ApiServer/ApiClient 全链路远程调用能力。本次测试表明:服务端纯处理能力达 1284 万 RPC/s,TCP 端到端吞吐量达 26.3 万 RPC/s(100 连接),单次无参调用延迟仅 52 μs,具备优秀的高并发扩展性。


测试环境

项目

OS

Windows 10 (10.0.19045)

CPU

Intel Core i9-10900K @ 3.70GHz (10 核 20 线程)

Runtime

.NET 10.0.3, X64 RyuJIT x86-64-v3

BenchmarkDotNet

v0.15.8

GC

Non-concurrent Workstation

SIMD

AVX2 VectorSize=256


测试方法

BenchmarkDotNet 基准测试(RpcBenchmarks)

  • 对 ApiClient → ApiServer 全链路 TCP 调用进行精确微基准测试
  • 每个并发级别创建独立客户端连接,调用服务端 BenchController
  • 测试场景:无参返回 Int32、String 回显(16/2000 字符)、多参数调用、IPacket 回显(16/2000 字节)
  • 并发级别:1、4、16、20(= CPU 线程数)、32

服务端纯处理能力测试(Direct)

  • 绕过 TCP 网络栈,直接调用 ApiServer.Process() 测量服务端原始处理能力
  • 20 线程并发,预热 3 秒 + 正式测试 10 秒

网络吞吐量压力测试(Throughput)

  • 100 个 TCP 客户端连接持续并发调用,测量端到端吞吐量和平均延迟
  • 预热 3 秒 + 正式测试 10 秒

测试结果

1. BenchmarkDotNet 基准测试

场景

并发数

Mean

Error

StdDev

Gen0

Gen1

Allocated

无参返回Int32

1

52.39 μs

201.184 μs

11.028 μs

0.2441

-

3.22 KB

String出入参(16字符)

1

53.60 μs

203.216 μs

11.139 μs

0.4883

-

5.60 KB

String出入参(2000字符)

1

116.12 μs

86.841 μs

4.760 μs

3.6621

-

34.91 KB

多基础类型参数

1

53.56 μs

88.185 μs

4.834 μs

0.8545

-

8.92 KB

IPacket出入参(16字节)

1

56.20 μs

234.207 μs

12.838 μs

0.2441

-

3.38 KB

IPacket出入参(2000字节)

1

62.59 μs

296.472 μs

16.251 μs

0.4883

-

5.32 KB

无参返回Int32

4

126.83 μs

316.883 μs

17.369 μs

0.9766

-

11.90 KB

String出入参(16字符)

4

109.51 μs

510.389 μs

27.976 μs

1.9531

-

21.45 KB

String出入参(2000字符)

4

116.69 μs

35.898 μs

1.968 μs

14.4043

1.4648

138.18 KB

多基础类型参数

4

122.94 μs

758.092 μs

41.554 μs

3.1738

-

34.72 KB

IPacket出入参(16字节)

4

130.50 μs

296.237 μs

16.238 μs

0.9766

-

12.57 KB

IPacket出入参(2000字节)

4

125.15 μs

521.811 μs

28.602 μs

1.9531

-

20.32 KB

无参返回Int32

16

277.94 μs

1,141.172 μs

62.551 μs

3.4180

-

46.63 KB

String出入参(16字符)

16

262.05 μs

48.643 μs

2.666 μs

6.3477

-

84.83 KB

String出入参(2000字符)

16

401.01 μs

1,455.394 μs

79.775 μs

50.7813

10.7422

551.75 KB

多基础类型参数

16

408.00 μs

2,450.887 μs

134.341 μs

9.7656

-

137.90 KB

IPacket出入参(16字节)

16

247.13 μs

341.186 μs

18.702 μs

3.4180

-

49.33 KB

IPacket出入参(2000字节)

16

280.44 μs

937.254 μs

51.374 μs

5.8594

0.4883

80.33 KB

无参返回Int32

20

279.41 μs

18.081 μs

0.991 μs

3.9063

-

58.22 KB

String出入参(16字符)

20

434.32 μs

2,162.381 μs

118.527 μs

6.8359

-

105.96 KB

String出入参(2000字符)

20

423.50 μs

18.243 μs

1.000 μs

61.5234

15.6250

689.62 KB

多基础类型参数

20

462.79 μs

2,810.361 μs

154.045 μs

11.7188

-

172.30 KB

IPacket出入参(16字节)

20

297.97 μs

1.147 μs

0.063 μs

3.9063

-

61.58 KB

IPacket出入参(2000字节)

20

NA

NA

NA

NA

NA

NA

无参返回Int32

32

520.81 μs

2,181.539 μs

119.578 μs

4.8828

-

92.95 KB

String出入参(16字符)

32

467.86 μs

27.738 μs

1.520 μs

8.7891

-

169.32 KB

String出入参(2000字符)

32

852.96 μs

4,170.590 μs

228.604 μs

76.1719

19.5313

1,103.16 KB

多基础类型参数

32

791.80 μs

5,006.656 μs

274.432 μs

13.6719

-

275.47 KB

IPacket出入参(16字节)

32

499.77 μs

1,410.822 μs

77.332 μs

4.8828

-

98.32 KB

IPacket出入参(2000字节)

32

NA

NA

NA

NA

NA

NA

注意:IPacket(2000字节) 在并发 20 和 32 时出现异常(NA),可能与高并发下大数据包的内存竞争有关。

2. 服务端纯处理能力测试(绕过 TCP,20 线程)

场景

总请求数

耗时

吞吐量

每请求分配

GC Gen0

NoArg_ReturnInt32

128,533,012

10.01 秒

12,844,229 RPC/s

0 B

9,115

EchoPacket_16B

96,516,183

10.01 秒

9,642,161 RPC/s

0 B

6,999

3. 网络吞吐量压力测试(TCP 端到端,100 连接)

场景

总请求数

耗时

吞吐量

平均延迟

GC Gen0

GC Gen1

NoArg_ReturnInt32

2,638,607

10.03 秒

263,184 RPC/s

380.0 μs

707

0

EchoPacket_16B

2,338,682

10.00 秒

233,812 RPC/s

427.7 μs

608

7


核心指标

单次调用延迟(BenchmarkDotNet,单线程)

场景

延迟

内存分配

等效 QPS

无参返回Int32

52.39 μs

3.22 KB

~19,088

String回显(16字符)

53.60 μs

5.60 KB

~18,657

String回显(2000字符)

116.12 μs

34.91 KB

~8,612

多基础类型参数

53.56 μs

8.92 KB

~18,671

IPacket回显(16字节)

56.20 μs

3.38 KB

~17,794

IPacket回显(2000字节)

62.59 μs

5.32 KB

~15,977

服务端纯处理峰值

指标

NoArg 峰值

1284 万 RPC/s

EchoPacket_16B 峰值

964 万 RPC/s

每请求堆分配

0 B(已完全池化)

TCP 端到端峰值(100 连接)

指标

NoArg 峰值

26.3 万 RPC/s

EchoPacket_16B 峰值

23.4 万 RPC/s

NoArg 平均延迟

380 μs

EchoPacket_16B 平均延迟

428 μs


对比分析

纵向分析:并发扩展性

无参返回Int32为例,观察不同并发级别的性能变化:

并发数

Mean

每连接延迟

等效总 QPS

扩展效率

1

52.39 μs

52.39 μs

19,088

基准

4

126.83 μs

31.71 μs

31,540

41.4%

16

277.94 μs

17.37 μs

57,566

18.9%

20

279.41 μs

13.97 μs

71,579

18.8%

32

520.81 μs

16.28 μs

61,449

10.1%

  • 最优并发点为 20(= CPU 线程数),总吞吐量约 7.2 万 QPS,每连接延迟最低 13.97 μs
  • 并发 32 超过 CPU 线程数后,总延迟上升 86%,等效 QPS 反而下降,出现线程竞争
  • 并发 1→4 扩展效率最高(41.4%),4→16 仍有显著提升,超过核心数后收益递减

横向分析:不同场景对比(并发 = 1)

场景

延迟

vs 基准

内存

vs 基准

无参返回Int32(基准)

52.39 μs

-

3.22 KB

-

String回显(16字符)

53.60 μs

+2.3%

5.60 KB

+73.9%

多基础类型参数

53.56 μs

+2.2%

8.92 KB

+177.0%

IPacket回显(16字节)

56.20 μs

+7.3%

3.38 KB

+5.0%

IPacket回显(2000字节)

62.59 μs

+19.5%

5.32 KB

+65.2%

String回显(2000字符)

116.12 μs

+121.6%

34.91 KB

+984.5%

  • IPacket 是最高效的数据传输方式:2000 字节 IPacket 仅比无参慢 19.5%,而 2000 字符 String 慢 121.6%
  • IPacket 2000 字节内存分配仅 5.32 KB,String 2000 字符高达 34.91 KB(6.6 倍差距)
  • 小数据量(≤16 字符/字节)时各场景延迟差异不大(2~7%),RPC 框架开销占主导

处理瓶颈分析:纯处理 vs TCP 端到端

指标

纯处理(Direct)

TCP 端到端

TCP 开销占比

NoArg 吞吐量

12,844,229 RPC/s

263,184 RPC/s

97.95%

EchoPacket_16B 吞吐量

9,642,161 RPC/s

233,812 RPC/s

97.58%

  • TCP 网络栈开销占总延迟的 97%~98%,服务端处理本身极轻
  • 纯处理场景已实现零堆分配(0 B/req),GC 压力来自消息编解码的临时对象

性能瓶颈定位

  1. TCP 网络栈是主要瓶颈:纯处理 1284 万 vs TCP 端到端 26.3 万,差距约 49 倍。TCP 的系统调用、缓冲区拷贝、异步回调是主要开销
  1. 大 String 序列化开销显著:2000 字符 String 的延迟是无参的 2.2 倍,内存分配是 10.8 倍,JSON 序列化/反序列化是主因
  1. IPacket 大数据包高并发不稳定:2000 字节 IPacket 在并发 ≥20 时出错(NA),可能存在缓冲区竞争或 Dispose 时序问题
  1. 超核心数并发收益递减:并发 32 时性能反而低于并发 20,线程上下文切换和锁竞争成为瓶颈

优化建议

优先级

建议

预期收益

P0

排查 IPacket(2000B) 高并发异常:在并发 ≥20 时复现错误,检查 ArrayPacket/OwnerPacket 的线程安全和 Dispose 时序

消除高并发大包场景的不稳定性

P1

启用连接复用(Multiplex):当前代码已注释,恢复后可在单连接上并行处理多请求,减少连接管理开销

TCP 吞吐量预计提升 30~50%

P2

IO 优化:考虑使用 System.IO.Pipelines 替代传统 Stream 模型,减少缓冲区拷贝

降低 TCP 层开销,缩小纯处理与端到端差距

P3

大 String 场景优化:对于大文本传输,建议业务侧先转为 byte[] 或 IPacket,避免 JSON 序列化/反序列化的内存开销

大文本延迟降低约 50%,内存分配降低约 85%

P4

池化消息对象:在 BenchmarkDotNet 测试中观察到 Gen0 GC 随并发线性增长,可进一步池化 DefaultMessage 及其 Payload

降低 GC 压力,改善 P99 延迟抖动