188 lines
5.2 KiB
Markdown
188 lines
5.2 KiB
Markdown
# 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/op,0 分配
|
||
- 顺序分配:27.42 ns/op,0 分配
|
||
|
||
### 4. 对象池优化(已存在)
|
||
|
||
**文件**: `pkg/port/iscsit/cmd.go`
|
||
|
||
- `ISCSICommand` 对象池:10.76 ns/op,0 分配
|
||
- 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 场景下。
|