hotload 1.2.0

Zero cost hot update dynamic library; supporting DLL, SO
Documentation
# hotload 基准测试报告


日期:2026-03-13

## 目标

测量 **hotload **(master 分支,本地路径依赖)在 **release/生产口径** 下,跨 DLL 调用各类参数传递的性能开销(ns/call)。

## 测试环境

- hotload 版本:last(`hotload = { path = "../../" }`- 平台:Windows 11 Pro
- 构建:全链路 release,exe & dll =>(`cargo build --release` + `cargo run --release -- bench`- 调用者:hotload\examples\test_1\ -> `test_1.exe`
- 动态库:`examples/call_lib``call_lib.dll`
- 内部容器:`UA<T>` 使用 `Cell<*mut T>` + `Arc` 原始指针管理(无 RefCell、无 AtomicPtr)

## 测试口径

| 参数 ||
|------|-----|
| warmup | 1 秒 |
| measure | 3 秒 |
| rounds | 3 轮 |
| 循环控制 | `AtomicBool::load(Relaxed)` — 无锁热循环 |
| 统计 | `ops/s = 迭代次数 / 实际耗时`,3 轮平均 |
| ns/call | `1,000,000,000 / ops_per_sec` |

## 测试方法


| 函数 | 参数数据量 | 说明 |
|------|-----------|------|
| `perf_add_i32(a: i32, b: i32) -> i32` | 8B | 基准:纯调用开销 |
| `perf_take_1k_by_ref(s: &PerfStruct1K) -> u64` | 1KB ref | 1KB 结构体传引用 |
| `perf_take_1k_by_val(s: PerfStruct1K) -> u64` | 1KB val | 1KB 结构体传值(拷贝) |
| `perf_take_5k_by_ref(s: &PerfStruct5K) -> u64` | 5KB ref | 5KB 结构体传引用 |
| `perf_take_5k_by_val(s: PerfStruct5K) -> u64` | 5KB val | 5KB 结构体传值(拷贝) |
| `perf_take_str(s: &str) -> usize` | &str | 字符串切片 |
| `perf_take_string_ref(s: &String) -> usize` | &String | String 引用 |
| `perf_sum_u64_1024(arr: &[u64;1024]) -> u64` | 8KB ref | 固定大小数组引用 |
| `perf_sum_slice_u64(slice: &[u64]) -> u64` | &[u64] | 动态切片 |

### 结构体定义

```rust
// Exactly 1024 bytes (1 KB)
#[repr(C)]

pub struct PerfStruct1K {
    pub tag: u64,           // 8 bytes
    pub data: [u8; 1016],   // 1016 bytes → total 1024
}

// Exactly 5120 bytes (5 KB)
#[repr(C)]

pub struct PerfStruct5K {
    pub tag: u64,           // 8 bytes
    pub data: [u8; 5112],   // 5112 bytes → total 5120
}
```

## 单线程测试结果


| 方法 | 数据量 | ops/s | ns/call |
|------|-------:|------:|--------:|
| `perf_add_i32` | 8B | 1,074,339,863 | **0.93** |
| `perf_take_1k_by_ref` | 1KB ref | 899,631,612 | **1.11** |
| `perf_take_1k_by_val` | 1KB val | 162,889,122 | **6.14** |
| `perf_take_5k_by_ref` | 5KB ref | 901,101,326 | **1.11** |
| `perf_take_5k_by_val` | 5KB val | 44,144,646 | **22.65** |
| `perf_take_str` | &str | 1,079,404,995 | **0.93** |
| `perf_take_string_ref` | &String | 1,077,253,291 | **0.93** |
| `perf_sum_u64_1024` | 8KB ref | 1,020,746,817 | **0.98** |
| `perf_sum_slice_u64` | &[u64] | 393,942,240 | **2.54** |

### 总体统计

| 指标 ||
|------|---:|
| mean ops/s | 739,272,657 |
| geo_mean ops/s | 523,011,746 |
| **mean ns/call** | **1.35 ns** |

## 多线程测试结果


### 测试口径

| 参数 ||
|------|-----|
| 线程数 | 1, 2, 4, 8 |
| 线程同步 | `Barrier` 统一起跑 |

#### 场景 1:同一 API — `perf_add_i32`(纯调用开销)

| Threads | Total ops/s | Per-thread ops/s | ns/call | Scale |
|--------:|------------:|-----------------:|--------:|------:|
| 1 | 909,726,339 | 909,726,339 | 1.10 | 1.00x |
| 2 | 1,692,572,884 | 846,286,442 | 1.18 | 1.86x |
| 4 | 3,397,557,724 | 849,389,431 | 1.18 | 3.73x |
| 8 | 6,565,696,943 | 820,712,118 | 1.22 | 7.22x |

#### 场景 2:同一 API — `perf_take_1k_by_ref`(1KB 传引用)

| Threads | Total ops/s | Per-thread ops/s | ns/call | Scale |
|--------:|------------:|-----------------:|--------:|------:|
| 1 | 799,023,906 | 799,023,906 | 1.25 | 1.00x |
| 2 | 1,523,776,279 | 761,888,139 | 1.31 | 1.91x |
| 4 | 3,002,126,875 | 750,531,719 | 1.33 | 3.76x |
| 8 | 5,812,845,095 | 726,605,637 | 1.38 | 7.27x |

#### 场景 3:同一 API — `perf_take_5k_by_val`(5KB 传值拷贝)

| Threads | Total ops/s | Per-thread ops/s | ns/call | Scale |
|--------:|------------:|-----------------:|--------:|------:|
| 1 | 45,851,934 | 45,851,934 | 21.81 | 1.00x |
| 2 | 87,455,052 | 43,727,526 | 22.87 | 1.91x |
| 4 | 172,272,690 | 43,068,173 | 23.22 | 3.76x |
| 8 | 335,024,614 | 41,878,077 | 23.88 | 7.31x |

#### 场景 4:混合 API — `add/1k_ref/1k_val/5k_ref/5k_val`

| Threads | Total ops/s | Per-thread ops/s | ns/call | Scale |
|--------:|------------:|-----------------:|--------:|------:|
| 1 | 910,544,278 | 910,544,278 | 1.10 | 1.00x |
| 2 | 1,627,697,473 | 813,848,736 | 1.23 | 1.79x |
| 4 | 2,560,701,762 | 640,175,441 | 1.56 | 2.81x |
| 8 | 4,209,638,554 | 526,204,819 | 1.90 | 4.62x |

## 结论


### 调用开销分析

| 场景 | ns/call | 说明 |
|------|--------:|------|
| **纯调用基准** | ~0.93 ns | `perf_add_i32` 仅传 2 个 i32,代表 hotload 跨 DLL 函数调用的固有开销 |
| **传引用(任意大小)** | ~1.1 ns | 无论 1KB、5KB 还是 8KB,by_ref 始终 ≈ 1.1 ns(只传指针,与数据大小无关) |
| **1KB 传值** | ~6.1 ns | 比基准多 ~5.2 ns,为 1KB 内存拷贝开销 |
| **5KB 传值** | ~22.7 ns | 比基准多 ~21.7 ns,为 5KB 内存拷贝开销 |

### 核心结论

1. **hotload 跨 DLL 调用开销 < 1 ns**(纯函数调用 0.93ns,函数指针解引用 + 间接调用)
2. **传引用零额外开销**:by_ref 与数据大小完全无关,始终 ≈ 1.1 ns
3. **传值额外开销 ≈ 4.2 ns/KB**:线性增长,由内存拷贝决定
4. **10 亿次/秒**:多个传引用场景下单线程吞吐突破 1G ops/s
5. **多线程近线性扩展**:8 线程总吞吐达 65.7 亿次/秒(纯调用),扩展因子 7.22x

## 如何复现

```bash
# 构建动态库

cd examples/call_lib
cargo build --release

# 运行单线程基准测试

cd examples/test_1
cargo run --release -- bench

# 运行多线程基准测试

cargo run --release -- bench_mt
```