性能基准测试结果
所有基准测试均在运行 bleRPC 固件并启用端到端加密(AES-128-GCM)的 nRF54L15 DK peripheral 上进行。MTU 协商为 247 字节。所有 Central 协商得到的连接间隔均为 15ms。
吞吐量对比
Python (macOS)
CI: 15ms
iOS (iPhone 16)
CI: 15ms
Android (Pixel 5)
CI: 15ms
Web (Chrome, macOS)
CI: 15ms
各平台详细结果
Python Central (macOS, bleak)
连接间隔:15ms(macOS 协商)— 测试代码
| 基准测试 | 结果 | 详细信息 |
|---|---|---|
| flash_read_throughput | 56.6 KB/s | 10 次 8192 字节,141.3 ms/call |
| flash_read_overhead | 30.1 ms/call | 1 字节 x 20 次 |
| echo_roundtrip | 30.0 ms/call | 50 次 |
| data_write_throughput | 6.5 KB/s | 200 字节 x 20 次,30.1 ms/call |
| counter_stream (P→C) | 3.3 ms/item | 20 条,共 66 ms |
| counter_upload (C→P) | 2.9 ms/item | 20 条,共 59 ms |
| 功能测试 | 结果 | 详细信息 |
|---|---|---|
| echo_basic | 通过 | |
| echo_empty | 通过 | |
| echo_max_length | 通过 | 256 字符字符串 |
| flash_read_basic | 通过 | 16 字节 |
| flash_read_8kb | 通过 | 8192 字节 |
| data_write_basic | 通过 | 1024 字节 |
| data_write_8kb | 通过 | 8192 字节 |
| multi_container_echo | 通过 | 250 字符字符串 |
| counter_stream | 通过 | 5 条 |
| counter_stream_large | 通过 | 20 条 |
| counter_upload | 通过 | 5 条 |
| counter_upload_large | 通过 | 20 条 |
iOS Central (iPhone 16, CoreBluetooth)
连接间隔:15ms(iOS 协商)— 测试代码
| 基准测试 | 结果 | 详细信息 |
|---|---|---|
| flash_read_throughput | 55.5 KB/s | 10 次 8192 字节,144.1 ms/call |
| flash_read_overhead | 30.0 ms/call | 1 字节 x 20 次 |
| echo_roundtrip | 30.6 ms/call | 50 次 |
| data_write_throughput | 6.4 KB/s | 200 字节 x 20 次,30.6 ms/call |
| counter_stream (P→C) | 3.1 ms/item | 20 条,共 62 ms |
| counter_upload (C→P) | 3.8 ms/item | 20 条,共 75 ms |
| 功能测试 | 结果 | 详细信息 |
|---|---|---|
| echo_basic | 通过 | |
| echo_empty | 通过 | |
| flash_read_basic | 通过 | 64 字节 |
| flash_read_8kb | 通过 | 8192 字节 |
| data_write | 通过 | 64 字节 |
| counter_stream | 通过 | 5 条 |
| counter_upload | 通过 | 5 条 |
Android Central (Pixel 5)
连接间隔:15ms(Android 协商)— 测试代码
| 基准测试 | 结果 | 详细信息 |
|---|---|---|
| flash_read_throughput | 83.5 KB/s | 10 次 8192 字节,95.8 ms/call |
| flash_read_overhead | 38.4 ms/call | 1 字节 x 20 次 |
| echo_roundtrip | 38.2 ms/call | 50 次 |
| data_write_throughput | 4.8 KB/s | 200 字节 x 20 次,40.7 ms/call |
| counter_stream (P→C) | 2.8 ms/item | 20 条,共 55 ms |
| counter_upload (C→P) | 5.9 ms/item | 20 条,共 118 ms |
| 功能测试 | 结果 | 详细信息 |
|---|---|---|
| echo_basic | 通过 | |
| echo_empty | 通过 | |
| flash_read_basic | 通过 | 64 字节 |
| flash_read_8kb | 通过 | 8192 字节 |
| data_write | 通过 | 64 字节 |
| counter_stream | 通过 | 5 条 |
| counter_upload | 通过 | 5 条 |
Web Central (Chrome, macOS, Web Bluetooth)
连接间隔:15ms(macOS 协商)— 测试代码
| 基准测试 | 结果 | 详细信息 |
|---|---|---|
| flash_read_throughput | 57.4 KB/s | 10 次 8192 字节,139.3 ms/call |
| flash_read_overhead | 30.0 ms/call | 1 字节 x 20 次 |
| echo_roundtrip | 30.0 ms/call | 50 次 |
| data_write_throughput | 6.5 KB/s | 200 字节 x 20 次,30.0 ms/call |
| counter_stream (P→C) | 3.1 ms/item | 20 条,共 61 ms |
| counter_upload (C→P) | 2.8 ms/item | 20 条,共 56 ms |
使用 central_web 演示(Chrome、加密、MTU 247)测量。与 Python(macOS)central 结果接近——传输无关的协议层(@blerpc/protocol-ts)在浏览器中原样运行。
性能说明
连接间隔的影响
连接间隔(CI)是影响往返延迟的主要因素。一次请求–响应循环需要两个连接事件(Central 写入请求、peripheral 发出通知)。固件把首选间隔固定为 15ms(PREF_MIN_INT = PREF_MAX_INT = 12),所有测试过的 Central 都接受:
- macOS (bleak):CI = 15ms → 30.0ms 往返
- iOS (iPhone 16):CI = 15ms → 30.6ms 往返
- Android (Pixel 5):CI = 15ms → 38.2ms 往返,但由于每个连接事件发送更多数据包,吞吐量最高
- Web (Chrome, macOS):CI = 15ms → 30.0ms 往返(与 Python 相同的传输和协议层)
真正让你付出代价的是请求一个区间。此前使用 15–30ms 区间(PREF_MAX_INT=24)时,每个 Central 都会选择更省电的一端即 30ms,往返时间因此翻倍到约 60ms。那个 30ms 从来不是操作系统的下限,而是 peripheral 自己请求的结果。有两件事并没有成为障碍:Android 无需调用 requestConnectionPriority(CONNECTION_PRIORITY_HIGH),因为无论应用设置的连接优先级如何,peripheral 自身的参数更新请求都会被接受;iPhone 上也没有出现 Apple QA1931 所警告的「把 15ms 请求放大回 30ms」的行为。代价是功耗上升。
复现时请注意:peripheral 在连接后 CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT(5 秒)才发出参数更新请求,在此之前使用的是 Central 的初始值(macOS 与 iOS 为 30ms,Pixel 5 为 45ms)。上述所有基准测试都会在连接后等待 5.5 秒再开始,因此测到的是协商后的间隔而非 Central 的初始值。
加密开销
端到端加密每个事务增加 20 字节(4 字节计数器 + 16 字节 AES-GCM 标签)。对于大载荷(8 KB),此开销可以忽略不计(<0.3%)。AES-128-GCM 加密/解密本身在现代移动设备上是硬件加速的,即使对于 8 KB 载荷也只需 <1ms
吞吐量优化
- nanopb FT_CALLBACK:将 flash 数据直接流式传入 protobuf 编码,节省 4KB 静态 RAM
- 零拷贝响应:Protobuf 响应直接在容器头偏移处编码
- 无包间延迟:容器背靠背发送
- 每次 flash read 最大 8KB:延迟和吞吐量之间的最佳平衡