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

5.2 KiB
Raw Blame History

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 零分配函数
// 优化前:动态分配切片
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 直接在缓冲区写入
// 优化前:多次分配和复制
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适合频繁分配/释放场景)

端到端测试验证

测试覆盖

$ 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

代码质量检查

$ 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 的大小:

// 在 daemon 启动时预分配
func init() {
    // 预分配 command pool
    for i := 0; i < 1000; i++ {
        cmd := &ISCSICommand{}
        commandPool.Put(cmd)
    }
}

2. 批处理优化

对于大量小 I/O考虑启用 iSCSI 多重命令和队列深度:

{
  "MaxQueueCommand": 64,
  "MaxOutstandingR2T": 4
}

3. 日志级别

生产环境使用 infowarn 级别:

./gotgt daemon --log warn

后续优化方向

  1. 零拷贝 I/O: 使用 splicesendfile 系统调用
  2. 批量提交: SCSI 命令批量提交减少锁竞争
  3. NUMA 感知: 多 socket 系统上的内存分配优化
  4. 内核旁路: 考虑 DPDK 或 io_uring 支持

结论

本次优化显著提升了 gotgt 的协议处理性能:

  • 关键路径延迟降低 15-60%
  • 内存分配减少 87.5%(序列化操作)
  • MarshalUint32/MarshalUint64 零分配
  • 所有测试通过race detector 无警告
  • go vet 无警告

这些改进将直接转化为更高的 IOPS 和更低的延迟,特别是在高并发小 I/O 场景下。