diff --git a/test/benchmarks/fs/fio_test.go b/test/benchmarks/fs/fio_test.go index 812813f75..71e1d0e44 100644 --- a/test/benchmarks/fs/fio_test.go +++ b/test/benchmarks/fs/fio_test.go @@ -18,7 +18,6 @@ import ( "fmt" "os" "path/filepath" - "strconv" "strings" "testing" @@ -34,92 +33,116 @@ import ( func BenchmarkFio(b *testing.B) { testCases := []tools.Fio{ { - Test: "write", - BlockSize: 4, - IODepth: 4, + Test: "write", + IOEngine: tools.EngineSync, + BlockSizeKB: 4, + IODepth: 1, }, { - Test: "write", - BlockSize: 64, - IODepth: 4, + Test: "write", + IOEngine: tools.EngineSync, + BlockSizeKB: 64, + IODepth: 1, }, { - Test: "write", - BlockSize: 1024, - IODepth: 4, + Test: "write", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 1024, + IODepth: 4, }, { - Test: "read", - BlockSize: 4, - IODepth: 4, + Test: "read", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 4, + IODepth: 4, }, { - Test: "read", - BlockSize: 64, - IODepth: 4, + Test: "read", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 64, + IODepth: 4, }, { - Test: "read", - BlockSize: 1024, - IODepth: 4, + Test: "read", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 1024, + IODepth: 4, }, { - Test: "randwrite", - BlockSize: 4, - IODepth: 4, + Test: "randwrite", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 4, + IODepth: 4, }, { - Test: "randread", - BlockSize: 4, - IODepth: 4, + Test: "randread", + IOEngine: tools.EngineLibAIO, + BlockSizeKB: 4, + IODepth: 4, }, { - Test: "write", - BlockSize: 4, - IODepth: 4, - Direct: true, + Test: "write", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 4, + IODepth: 4, + Direct: true, }, { - Test: "write", - BlockSize: 64, - IODepth: 4, - Direct: true, + Test: "write", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 64, + IODepth: 4, + Direct: true, }, { - Test: "write", - BlockSize: 1024, - IODepth: 4, - Direct: true, + Test: "write", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 1024, + IODepth: 4, + Direct: true, }, { - Test: "read", - BlockSize: 4, - IODepth: 4, - Direct: true, + Test: "read", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 4, + IODepth: 4, + Direct: true, }, { - Test: "read", - BlockSize: 64, - IODepth: 4, - Direct: true, + Test: "read", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 64, + IODepth: 4, + Direct: true, }, { - Test: "read", - BlockSize: 1024, - IODepth: 4, - Direct: true, + Test: "read", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 1024, + IODepth: 4, + Direct: true, }, { - Test: "randwrite", - BlockSize: 4, - IODepth: 4, - Direct: true, + Test: "randwrite", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 4, + IODepth: 4, + Direct: true, }, { - Test: "randread", - BlockSize: 4, - IODepth: 4, - Direct: true, + Test: "randread", + IOEngine: tools.EngineLibAIO, + Jobs: 8, + BlockSizeKB: 4, + IODepth: 4, + Direct: true, }, } @@ -131,29 +154,14 @@ func BenchmarkFio(b *testing.B) { for _, fsType := range []harness.FileSystemType{harness.BindFS, harness.TmpFS, harness.RootFS} { for _, tc := range testCases { - operation := tools.Parameter{ - Name: "operation", - Value: tc.Test, - } - blockSize := tools.Parameter{ - Name: "blockSize", - Value: fmt.Sprintf("%dK", tc.BlockSize), - } - directIO := tools.Parameter{ - Name: "directIO", - Value: strconv.FormatBool(tc.Direct), - } filesystem := tools.Parameter{ Name: "filesystem", Value: string(fsType), } - name, err := tools.ParametersToName(operation, blockSize, directIO, filesystem) - if err != nil { - b.Fatalf("Failed to parser paramters: %v", err) - } + _, name := tc.Parameters(b, filesystem) b.Run(name, func(b *testing.B) { b.StopTimer() - tc.Size = b.N + tc.SizeMB = b.N ctx := context.Background() container := machine.GetContainer(ctx, b) @@ -200,7 +208,7 @@ func BenchmarkFio(b *testing.B) { // For reads, we need a file to read so make one inside the container. if strings.Contains(tc.Test, "read") { - fallocateCmd := fmt.Sprintf("fallocate -l %dM %s", tc.Size, outfile) + fallocateCmd := fmt.Sprintf("fallocate -l %dM %s", tc.SizeMB, outfile) if out, err := container.Exec(ctx, dockerutil.ExecOpts{}, strings.Split(fallocateCmd, " ")...); err != nil { b.Fatalf("failed to create readable file on mount: %v, %s", err, out) diff --git a/test/benchmarks/tools/fio.go b/test/benchmarks/tools/fio.go index 5cd9aee97..d43187ac8 100644 --- a/test/benchmarks/tools/fio.go +++ b/test/benchmarks/tools/fio.go @@ -22,22 +22,42 @@ import ( "testing" ) +// IOEngine is a I/O engine name passed to `fio`. +type IOEngine string + +// Names of FIO I/O engines. +const ( + EngineSync = IOEngine("sync") + EngineLibAIO = IOEngine("libaio") +) + // Fio makes 'fio' commands and parses their output. type Fio struct { - Test string // test to run: read, write, randread, randwrite. - Size int // total size to be read/written in megabytes. - BlockSize int // block size to be read/written in kilobytes. - IODepth int // I/O depth for reads/writes. - Direct bool // Whether to use direct I/O (O_DIRECT) or not. + Test string // test to run: read, write, randread, randwrite. + IOEngine IOEngine // ioengine to use: sync or libaio. + SizeMB int // total size to be read/written in megabytes. + BlockSizeKB int // block size to be read/written in kilobytes. + IODepth int // I/O depth for reads/writes when using libaio. + Jobs int // Number of jobs running in concurrent threads. + Direct bool // Whether to use direct I/O (O_DIRECT) or not. } // MakeCmd makes a 'fio' command. func (f *Fio) MakeCmd(filename string) []string { - cmd := []string{"fio", "--output-format=json", "--ioengine=sync"} + cmd := []string{"fio", "--output-format=json"} cmd = append(cmd, fmt.Sprintf("--name=%s", f.Test)) - cmd = append(cmd, fmt.Sprintf("--size=%dM", f.Size)) - cmd = append(cmd, fmt.Sprintf("--blocksize=%dK", f.BlockSize)) + cmd = append(cmd, fmt.Sprintf("--ioengine=%s", string(f.IOEngine))) + if f.Jobs == 0 { + f.Jobs = 1 + } + cmd = append(cmd, fmt.Sprintf("--numjobs=%d", f.Jobs)) + cmd = append(cmd, fmt.Sprintf("--max-jobs=%d", f.Jobs)) + cmd = append(cmd, fmt.Sprintf("--size=%dM", f.SizeMB)) + cmd = append(cmd, fmt.Sprintf("--blocksize=%dK", f.BlockSizeKB)) cmd = append(cmd, fmt.Sprintf("--filename=%s", filename)) + if f.IODepth != 1 && f.IOEngine != EngineLibAIO { + panic(fmt.Sprintf("iodepth=%d does not make sense with ioengine=%q", f.IODepth, f.IOEngine)) + } cmd = append(cmd, fmt.Sprintf("--iodepth=%d", f.IODepth)) if f.Direct { cmd = append(cmd, "--direct=1") @@ -57,6 +77,42 @@ func (f *Fio) MakeCmd(filename string) []string { return cmd } +// Parameters returns the test parameters and the overall test name derived +// from them. +func (f *Fio) Parameters(b *testing.B, additional ...Parameter) ([]Parameter, string) { + b.Helper() + if f.Jobs == 0 { + f.Jobs = 1 + } + operation := Parameter{ + Name: "operation", + Value: f.Test, + } + ioEngine := Parameter{ + Name: "ioEngine", + Value: string(f.IOEngine), + } + jobs := Parameter{ + Name: "jobs", + Value: strconv.Itoa(f.Jobs), + } + blockSize := Parameter{ + Name: "blockSize", + Value: fmt.Sprintf("%dK", f.BlockSizeKB), + } + directIO := Parameter{ + Name: "directIO", + Value: strconv.FormatBool(f.Direct), + } + parameters := []Parameter{operation, ioEngine, jobs, blockSize, directIO} + parameters = append(parameters, additional...) + name, err := ParametersToName(parameters...) + if err != nil { + b.Fatalf("Failed to create parameter list: %v", err) + } + return parameters, name +} + // Report reports metrics based on output from an 'fio' command. func (f *Fio) Report(b *testing.B, output string) { b.Helper()