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  }