Files
gotgt/PERFORMANCE_REPORT.md
2026-03-14 11:45:35 +08:00

188 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 场景下。