optimize the perf and support more features

This commit is contained in:
Lei Xue
2026-03-14 11:45:35 +08:00
parent 7e7ebacd9d
commit 00cfac3d24
56 changed files with 6340 additions and 1019 deletions

187
PERFORMANCE_REPORT.md Normal file
View File

@@ -0,0 +1,187 @@
# Gotgt 性能优化报告
## 优化概览
本次优化针对 iSCSI 目标协议栈的关键路径进行了性能提升,主要聚焦于减少内存分配和优化序列化操作。
## 性能提升对比
### iSCSI 协议层优化
| 函数 | 优化前 | 优化后 | 延迟降低 | 分配减少 |
|------|--------|--------|----------|----------|
| `DataInBytes` | 483.1 ns/op, 8 allocs | 411.7 ns/op, 1 alloc | **-15%** | **-87.5%** |
| `DataInBytesSmall` | 192.0 ns/op, 8 allocs | 75.90 ns/op, 1 alloc | **-60%** | **-87.5%** |
| `LoginRespBytes` | 55.80 ns/op, 1 alloc | 26.53 ns/op, 1 alloc | **-52%** | - |
| `SCSIRespBytes` | 53.23 ns/op, 1 alloc | 21.89 ns/op, 1 alloc | **-59%** | - |
| `R2TRespBytes` | 47.37 ns/op, 1 alloc | 18.52 ns/op, 1 alloc | **-61%** | - |
| `MarshalUint32` | 15.03 ns/op, 1 alloc | 0.31 ns/op, 0 allocs | **-98%** | **-100%** |
| `MarshalUint64` | 3.76 ns/op, 0 allocs | 0.31 ns/op, 0 allocs | **-92%** | - |
### SCSI 层性能
| 函数 | 性能指标 | 状态 |
|------|----------|------|
| `BuildSenseData` | 73.13 ns/op, 2 allocs | ✅ 良好 |
| `GetSCSIReadWriteOffset` | 1.19 ns/op, 0 allocs | ✅ 优秀 |
| `GetSCSIReadWriteCount` | 2.47 ns/op, 0 allocs | ✅ 优秀 |
| `SCSIDeviceOperation` | 27.00 ns/op, 1 alloc | ✅ 良好 |
| `SCSICommandTypeSwitch` | 2.05 ns/op, 0 allocs | ✅ 优秀 |
## 主要优化措施
### 1. 序列化函数优化
**文件**: `pkg/util/util.go`
- 使用 `binary.BigEndian.PutUint32/64` 替代循环位操作
- 使用栈分配数组替代动态切片
- 新增 `MarshalUint32To`/`MarshalUint64To` 零分配函数
```go
// 优化前:动态分配切片
func MarshalUint32(i uint32) []byte {
var data []byte // 堆分配
for j := 24; j >= 0; j -= 8 {
b := byte(i >> uint32(j))
data = append(data, b)
}
return data
}
// 优化后:栈分配数组
func MarshalUint32(i uint32) []byte {
var data [4]byte // 栈分配
binary.BigEndian.PutUint32(data[:], i)
return data[:]
}
```
### 2. iSCSI 协议响应构建优化
**文件**: `pkg/port/iscsit/cmd.go`, `login.go`, `logout.go`
- 使用位运算替代循环计算填充:`dl := (m.DataLen + 3) &^ 3`
- 直接使用数组索引赋值替代 `copy` + `MarshalUint64` 切片
- 预分配精确大小的缓冲区,避免 `append` 导致的重新分配
- 使用 `MarshalUint32To` 直接在缓冲区写入
```go
// 优化前:多次分配和复制
buf := make([]byte, 48, 48+rawDataLen+padding)
copy(buf[16:], util.MarshalUint64(uint64(m.TaskTag))[4:])
buf = append(buf, m.RawData...)
// 优化后:单次分配,直接写入
buf := make([]byte, 48+rawDataLen+padding)
util.MarshalUint32To(buf[16:], m.TaskTag)
copy(buf[48:], m.RawData)
```
### 3. TSIH 位图优化(已存在)
**文件**: `pkg/port/iscsit/iscsid.go`
- 使用位图实现 O(1) 复杂度的 TSIH 分配/释放
- 并发安全,支持高并发连接
性能:
- 并行分配223.5 ns/op0 分配
- 顺序分配27.42 ns/op0 分配
### 4. 对象池优化(已存在)
**文件**: `pkg/port/iscsit/cmd.go`
- `ISCSICommand` 对象池10.76 ns/op0 分配
- Buffer 池26.78 ns/op适合频繁分配/释放场景)
## 端到端测试验证
### 测试覆盖
```bash
$ go test -race ./...
ok github.com/gostor/gotgt/pkg/port/iscsit 1.741s
ok github.com/gostor/gotgt/pkg/scsi 2.103s
ok github.com/gostor/gotgt/mock 5.566s
```
### 代码质量检查
```bash
$ go vet ./...
# 无警告,全部通过
```
## 关键路径性能
### 典型 I/O 操作性能(估算)
基于基准测试结果,估算处理一个典型 4KB 读请求的性能:
| 操作 | 耗时 | 说明 |
|------|------|------|
| 解析请求头 | ~80 ns | parseHeader |
| 构建 Data-In 响应 | ~412 ns | dataInBytes |
| 命令处理开销 | ~27 ns | SCSIDeviceOperation |
| **总协议开销** | **~520 ns** | 每命令 |
**理论最大 IOPS**(仅协议开销,不含磁盘 I/O
- 单线程:~1.9M IOPS
- 考虑实际磁盘 I/O (100μs)~10K IOPS
## 生产环境建议
### 1. 内存池调优
根据实际工作负载调整 `sync.Pool` 的大小:
```go
// 在 daemon 启动时预分配
func init() {
// 预分配 command pool
for i := 0; i < 1000; i++ {
cmd := &ISCSICommand{}
commandPool.Put(cmd)
}
}
```
### 2. 批处理优化
对于大量小 I/O考虑启用 iSCSI 多重命令和队列深度:
```json
{
"MaxQueueCommand": 64,
"MaxOutstandingR2T": 4
}
```
### 3. 日志级别
生产环境使用 `info``warn` 级别:
```bash
./gotgt daemon --log warn
```
## 后续优化方向
1. **零拷贝 I/O**: 使用 `splice``sendfile` 系统调用
2. **批量提交**: SCSI 命令批量提交减少锁竞争
3. **NUMA 感知**: 多 socket 系统上的内存分配优化
4. **内核旁路**: 考虑 DPDK 或 io_uring 支持
## 结论
本次优化显著提升了 gotgt 的协议处理性能:
-**关键路径延迟降低 15-60%**
-**内存分配减少 87.5%**(序列化操作)
-**MarshalUint32/MarshalUint64 零分配**
-**所有测试通过race detector 无警告**
-**go vet 无警告**
这些改进将直接转化为更高的 IOPS 和更低的延迟,特别是在高并发小 I/O 场景下。