github.com/KEINOS/go-countline@v1.1.1-0.20221217083629-60710df7606b/cl/benchmarks_test.go (about) 1 // To keep the repository size small, most of the test data is left uncommitted. 2 // You must generate it yourself before testing and benchmarking. 3 // 4 // ```shellsession 5 // $ # From the root of the repository 6 // $ go generate ./... 7 // ... 8 // ``` 9 // 10 //go:generate go run ./_gen 11 package cl 12 13 import ( 14 "fmt" 15 "io" 16 "os" 17 "path/filepath" 18 "strings" 19 "testing" 20 21 "github.com/KEINOS/go-countline/cl/_alt" 22 ) 23 24 // targetFuncions is a map of functions to be tested. 25 // We are using a map to avoid the order of the tests. 26 // 27 //nolint:gochecknoglobals 28 var targetFuncions = map[string]struct { 29 fn func(io.Reader) (int, error) 30 }{ 31 // Current implementation 32 "CountLinesCurr": {CountLines}, 33 // Alternate implementation. See alt_test.go. 34 "CountLinesAlt1": {_alt.CountLinesAlt1}, 35 "CountLinesAlt2": {_alt.CountLinesAlt2}, 36 "CountLinesAlt3": {_alt.CountLinesAlt3}, 37 "CountLinesAlt4": {_alt.CountLinesAlt4}, 38 "CountLinesAlt5": {_alt.CountLinesAlt5}, 39 } 40 41 // targetDatas is a list of files under `cl/testdata/` directory to be tested. 42 // We are using a map to avoid the order of the tests. 43 // 44 // These files are created via `go generate ...` command. See `cl/_gen/gen.go`. 45 // 46 //nolint:gochecknoglobals 47 var targetDatas = map[string]struct { 48 nameFile string 49 typeSize string 50 sizeFile int 51 numLine int 52 }{ 53 "Tiny": {nameFile: "data_Tiny.txt", typeSize: "medium", sizeFile: 1032, numLine: 114}, 54 "Small": {nameFile: "data_Small.txt", typeSize: "medium", sizeFile: 1048578, numLine: 88307}, 55 "Medium": {nameFile: "data_Medium.txt", typeSize: "medium", sizeFile: 10485767, numLine: 815144}, 56 "Large": {nameFile: "data_Large.txt", typeSize: "large", sizeFile: 52428802, numLine: 3824279}, 57 "Huge": {nameFile: "data_Huge.txt", typeSize: "large", sizeFile: 104857612, numLine: 7569194}, 58 "Giant": {nameFile: "data_Giant.txt", typeSize: "giant", sizeFile: 1073741832, numLine: 72323529}, 59 } 60 61 // ============================================================================ 62 // Benchmarks 63 // ============================================================================ 64 65 // Benchmark of 1GiB file. 66 func Benchmark_giant(b *testing.B) { 67 nameFile := "data_Giant.txt" 68 sizeFile := 1073741832 69 expectNumLine := 72323529 70 71 for nameFunc, targetFunc := range targetFuncions { 72 pathFile := filepath.Join("testdata", nameFile) 73 nameTest := fmt.Sprintf("size-%s_%s_%s", readableSize(sizeFile), "Gigantic", nameFunc) 74 75 for i := 0; i < b.N; i++ { 76 b.Run(nameTest, func(b *testing.B) { 77 runBench(b, expectNumLine, pathFile, targetFunc.fn) 78 }) 79 } 80 } 81 } 82 83 // Benchmark of light weight size files (Tiny, Small, Medium). 84 func Benchmark_light(b *testing.B) { 85 for _, data := range targetDatas { 86 nameData := strings.TrimSuffix(strings.TrimPrefix(data.nameFile, "data_"), ".txt") 87 88 for nameFunc, targetFunc := range targetFuncions { 89 if data.typeSize != "medium" { 90 continue 91 } 92 93 pathFile := filepath.Join("testdata", data.nameFile) 94 nameTest := fmt.Sprintf("size-%s_%s_%s", readableSize(data.sizeFile), nameData, nameFunc) 95 96 for i := 0; i < b.N; i++ { 97 b.Run(nameTest, func(b *testing.B) { 98 expectNumLine := data.numLine 99 runBench(b, expectNumLine, pathFile, targetFunc.fn) 100 }) 101 } 102 } 103 } 104 } 105 106 // Benchmark of heavy weight size files (Large, Huge, Giant). 107 func Benchmark_heavy(b *testing.B) { 108 for _, data := range targetDatas { 109 nameData := strings.TrimSuffix(strings.TrimPrefix(data.nameFile, "data_"), ".txt") 110 111 for nameFunc, targetFunc := range targetFuncions { 112 if data.typeSize != "large" { 113 continue 114 } 115 116 pathFile := filepath.Join("testdata", data.nameFile) 117 nameTest := fmt.Sprintf("size-%s_%s_%s", readableSize(data.sizeFile), nameData, nameFunc) 118 119 for i := 0; i < b.N; i++ { 120 b.Run(nameTest, func(b *testing.B) { 121 expectNumLine := data.numLine 122 runBench(b, expectNumLine, pathFile, targetFunc.fn) 123 }) 124 } 125 } 126 } 127 } 128 129 // ---------------------------------------------------------------------------- 130 // Helper functions 131 // ---------------------------------------------------------------------------- 132 133 //nolint:varnamelen // "fn" is short for the scope of its usage but allow it 134 func runBench(b *testing.B, expectNumLines int, pathFile string, fn func(io.Reader) (int, error)) { 135 b.Helper() 136 137 fileReader, err := os.Open(pathFile) 138 if err != nil { 139 b.Fatal(err) 140 } 141 defer fileReader.Close() 142 143 b.ResetTimer() // Begin benchmark 144 145 countLines, err := fn(fileReader) 146 if err != nil { 147 b.Fatal(err) 148 } 149 150 b.StopTimer() // End benchmark 151 152 expectLineCount := expectNumLines 153 actualLineCount := countLines 154 155 if expectLineCount != actualLineCount { 156 b.Fatalf( 157 "test %v failed: expect=%d, actual=%d", 158 b.Name(), expectLineCount, actualLineCount, 159 ) 160 } 161 } 162 163 func readableSize(value int) string { 164 switch { 165 case value >= 1024*1024*1024: 166 return fmt.Sprintf("%dGiB", value/(1024*1024*1024)) 167 case value >= 1024*1024: 168 return fmt.Sprintf("%dMiB", value/(1024*1024)) 169 case value >= 1024: 170 return fmt.Sprintf("%dKiB", value/1024) 171 default: 172 return fmt.Sprintf("%dByte", value) 173 } 174 }