Files
gotgt/pkg/port/iscsit/perf_test.go
2026-03-14 11:45:35 +08:00

404 lines
8.6 KiB
Go

/*
Copyright 2024 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iscsit
import (
"bytes"
"testing"
"time"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
// BenchmarkParseHeader benchmarks iSCSI protocol header parsing performance
func BenchmarkParseHeader(b *testing.B) {
// Build a typical SCSI CDB command header
header := make([]byte, BHS_SIZE)
header[0] = byte(OpSCSICmd) // SCSI Command
header[1] = 0x80 // Final bit
header[4] = 0 // AHS length
header[5] = 0
header[6] = 0
header[7] = 0 // Data segment length = 0
// TaskTag at bytes 16-19
header[16] = 0x00
header[17] = 0x00
header[18] = 0x00
header[19] = 0x01
// ExpectedDataLen at bytes 20-23
header[20] = 0x00
header[21] = 0x00
header[22] = 0x10
header[23] = 0x00 // 4096 bytes
// CmdSN at bytes 24-27
header[24] = 0x00
header[25] = 0x00
header[26] = 0x00
header[27] = 0x01
// ExpStatSN at bytes 28-31
header[28] = 0x00
header[29] = 0x00
header[30] = 0x00
header[31] = 0x01
// CDB at bytes 32-47 (READ_10 command)
header[32] = byte(api.READ_10)
header[33] = 0x00
header[34] = 0x00
header[35] = 0x00
header[36] = 0x00
header[37] = 0x00 // LBA = 0
header[38] = 0x00
header[39] = 0x08 // Transfer length = 8 blocks
header[40] = 0x00 // Control
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
cmd, err := parseHeader(header)
if err != nil {
b.Fatal(err)
}
_ = cmd
}
}
// BenchmarkParseHeaderWithPool benchmarks header parsing with object pool
func BenchmarkParseHeaderWithPool(b *testing.B) {
header := make([]byte, BHS_SIZE)
header[0] = byte(OpSCSICmd)
header[1] = 0x80
header[32] = byte(api.READ_10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
cmd := getCommand()
cmd.OpCode = OpCode(header[0] & ISCSI_OPCODE_MASK)
cmd.Final = 0x80&header[1] == 0x80
cmd.AHSLen = int(header[4]) * 4
cmd.DataLen = int(ParseUint(header[5:8]))
cmd.TaskTag = uint32(ParseUint(header[16:20]))
cmd.CDB = header[32:48]
cmd.StartTime = time.Now()
putCommand(cmd)
}
}
// BenchmarkDataInBytes benchmarks Data-In response serialization performance
func BenchmarkDataInBytes(b *testing.B) {
data := make([]byte, 4096)
for i := range data {
data[i] = byte(i % 256)
}
cmd := &ISCSICommand{
OpCode: OpSCSIIn,
Final: true,
FinalInSeq: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
DataLen: 4096,
DataSN: 0,
BufferOffset: 0,
RawData: data,
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = cmd.dataInBytes()
}
}
// BenchmarkDataInBytesSmall benchmarks Data-In performance with small data blocks
func BenchmarkDataInBytesSmall(b *testing.B) {
data := make([]byte, 512)
cmd := &ISCSICommand{
OpCode: OpSCSIIn,
Final: true,
FinalInSeq: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
DataLen: 512,
DataSN: 0,
BufferOffset: 0,
RawData: data,
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = cmd.dataInBytes()
}
}
// BenchmarkDataInBytesLarge benchmarks Data-In performance with large data blocks
func BenchmarkDataInBytesLarge(b *testing.B) {
data := make([]byte, 65536)
cmd := &ISCSICommand{
OpCode: OpSCSIIn,
Final: true,
FinalInSeq: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
DataLen: 65536,
DataSN: 0,
BufferOffset: 0,
RawData: data,
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = cmd.dataInBytes()
}
}
// BenchmarkBytesComparison compares Bytes() performance for different OpCodes
func BenchmarkBytesComparison(b *testing.B) {
testCases := []struct {
name string
cmd *ISCSICommand
}{
{
name: "LoginResp",
cmd: &ISCSICommand{
OpCode: OpLoginResp,
Final: true,
Transit: true,
CSG: LoginOperationalNegotiation,
NSG: FullFeaturePhase,
TaskTag: 1,
StatSN: 0,
ExpCmdSN: 1,
MaxCmdSN: 1,
StatusClass: 0,
StatusDetail: 0,
RawData: []byte("TargetPortalGroupTag=1"),
},
},
{
name: "SCSIResp",
cmd: &ISCSICommand{
OpCode: OpSCSIResp,
Final: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
},
},
{
name: "SCSIIn",
cmd: &ISCSICommand{
OpCode: OpSCSIIn,
Final: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
DataLen: 4096,
RawData: make([]byte, 4096),
},
},
{
name: "R2T",
cmd: &ISCSICommand{
OpCode: OpReady,
Final: true,
TaskTag: 1,
StatSN: 100,
ExpCmdSN: 101,
MaxCmdSN: 200,
R2TSN: 0,
BufferOffset: 0,
DesiredLength: 8192,
},
},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = tc.cmd.Bytes()
}
})
}
}
// BenchmarkCommandPool benchmarks command object pool performance
func BenchmarkCommandPool(b *testing.B) {
b.Run("WithPool", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
cmd := getCommand()
cmd.OpCode = OpSCSICmd
cmd.TaskTag = uint32(i)
putCommand(cmd)
}
})
b.Run("WithoutPool", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
cmd := &ISCSICommand{
OpCode: OpSCSICmd,
TaskTag: uint32(i),
}
_ = cmd
}
})
}
// BenchmarkBufferPool benchmarks buffer pool performance
func BenchmarkBufferPool(b *testing.B) {
b.Run("WithPool", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
buf := getBuffer()
buf[0] = byte(i)
putBuffer(buf)
}
})
b.Run("WithoutPool", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
buf := make([]byte, BHS_SIZE)
buf[0] = byte(i)
_ = buf
}
})
}
// BenchmarkTaskStateTransition benchmarks task state transition performance
func BenchmarkTaskStateTransition(b *testing.B) {
task := &iscsiTask{
tag: 1,
state: taskPending,
scmd: &api.SCSICommand{},
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
task.state = taskPending
} else {
task.state = taskSCSI
}
}
}
// BenchmarkParseUint benchmarks ParseUint performance
func BenchmarkParseUint(b *testing.B) {
testData := []byte{0x00, 0x00, 0x10, 0x00}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = ParseUint(testData)
}
}
// BenchmarkBytesComparisonEqual benchmarks byte comparison performance
func BenchmarkBytesComparisonEqual(b *testing.B) {
a := make([]byte, 48)
b2 := make([]byte, 48)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = bytes.Equal(a, b2)
}
}
// BenchmarkMarshalUint32 benchmarks uint32 serialization performance
func BenchmarkMarshalUint32(b *testing.B) {
val := uint32(0x12345678)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = util.MarshalUint32(val)
}
}
// BenchmarkMarshalUint64 benchmarks uint64 serialization performance
func BenchmarkMarshalUint64(b *testing.B) {
val := uint64(0x1234567890ABCDEF)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = util.MarshalUint64(val)
}
}
// BenchmarkBuildRespPackage benchmarks complete response package building performance
func BenchmarkBuildRespPackage(b *testing.B) {
conn := &iscsiConnection{
state: CONN_STATE_SCSI,
statSN: 99,
expCmdSN: 100,
loginParam: &iscsiLoginParam{
sessionParam: []ISCSISessionParam{
{idx: ISCSI_PARAM_MAX_BURST, Value: 262144},
},
},
session: &ISCSISession{
ExpCmdSN: 100,
MaxQueueCommand: 32,
},
req: &ISCSICommand{
OpCode: OpSCSICmd,
TaskTag: 1,
ExpStatSN: 100,
ExpectedDataLen: 4096,
StartTime: time.Now(),
},
rxTask: &iscsiTask{
tag: 1,
scmd: &api.SCSICommand{
Result: 0,
Direction: api.SCSIDataRead,
InSDBBuffer: &api.SCSIDataBuffer{
Buffer: make([]byte, 4096),
Length: 4096,
},
},
},
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = conn.buildRespPackage(OpSCSIResp, nil)
}
}