LCOV - code coverage report
Current view: top level - pebble/cmd/pebble - fsbench.go (source / functions) Hit Total Coverage
Test: 2025-01-08 08:16Z 80ce1d7f - tests only.lcov Lines: 62 443 14.0 %
Date: 2025-01-08 08:17:34 Functions: 0 0 -

          Line data    Source code
       1             : package main
       2             : 
       3             : import (
       4             :         "bytes"
       5             :         "fmt"
       6             :         "log"
       7             :         "os"
       8             :         "path"
       9             :         "sync"
      10             :         "sync/atomic"
      11             :         "time"
      12             : 
      13             :         "github.com/cockroachdb/errors"
      14             :         "github.com/cockroachdb/pebble/vfs"
      15             :         "github.com/spf13/cobra"
      16             : )
      17             : 
      18             : var fsBenchCmd = &cobra.Command{
      19             :         Use:   "fs <dir>",
      20             :         Short: "Run file system benchmarks.",
      21             :         Long: `
      22             : Run file system benchmarks. Each benchmark is predefined and can be
      23             : run using the command "bench fs <dir> --bench-name <benchmark>".
      24             : Each possible <benchmark> which can be run is defined in the code.
      25             : Benchmarks may require the specification of a --duration or
      26             : --max-ops flag, to prevent the benchmark from running forever
      27             : or running out of memory.
      28             : 
      29             : The --num-times flag can be used to run the entire benchmark, more than
      30             : once. If the flag isn't provided, then the benchmark is only run once.
      31             : `,
      32             :         Args: cobra.ExactArgs(1),
      33             :         RunE: runFsBench,
      34             : }
      35             : 
      36             : const writeBatchSize = 1 << 10
      37             : 
      38             : var fsConfig struct {
      39             :         // An upper limit on the number of ops which can be run.
      40             :         maxOps int
      41             : 
      42             :         // Benchmark to run.
      43             :         benchname string
      44             : 
      45             :         // Number of times each benchmark should be run.
      46             :         numTimes int
      47             : 
      48             :         fs vfs.FS
      49             : 
      50             :         precomputedWriteBatch []byte
      51             : }
      52             : 
      53           1 : func init() {
      54           1 :         fsBenchCmd.Flags().IntVar(
      55           1 :                 &fsConfig.maxOps, "max-ops", 0,
      56           1 :                 "Maximum number of times the operation which is being benchmarked should be run.",
      57           1 :         )
      58           1 : 
      59           1 :         fsBenchCmd.Flags().StringVar(
      60           1 :                 &fsConfig.benchname, "bench-name", "", "The benchmark to run.")
      61           1 :         fsBenchCmd.MarkFlagRequired("bench-name")
      62           1 : 
      63           1 :         fsBenchCmd.Flags().IntVar(
      64           1 :                 &fsConfig.numTimes, "num-times", 1,
      65           1 :                 "Number of times each benchmark should be run.")
      66           1 : 
      67           1 :         // Add subcommand to list
      68           1 :         fsBenchCmd.AddCommand(listFsBench)
      69           1 : 
      70           1 :         // Just use the default vfs implementation for now.
      71           1 :         fsConfig.fs = vfs.Default
      72           1 : 
      73           1 :         fsConfig.precomputedWriteBatch = bytes.Repeat([]byte("a"), writeBatchSize)
      74           1 : }
      75             : 
      76             : // State relevant to a benchmark.
      77             : type fsBench struct {
      78             :         // A short name for the benchmark.
      79             :         name string
      80             : 
      81             :         // A one line description for the benchmark.
      82             :         description string
      83             : 
      84             :         // numOps is the total number of ops which
      85             :         // have been run for the benchmark. This is used
      86             :         // to make sure that we don't benchmark the operation
      87             :         // more than max-ops times.
      88             :         numOps int
      89             : 
      90             :         // directory under which the benchmark is run.
      91             :         dir     vfs.File
      92             :         dirName string
      93             : 
      94             :         // Stats associated with the benchmark.
      95             :         reg *histogramRegistry
      96             : 
      97             :         // The operation which we're benchmarking. This
      98             :         // will be called over and over again.
      99             :         // Returns false if run should no longer be called.
     100             :         run func(*namedHistogram) bool
     101             : 
     102             :         // Stop the benchmark from executing any further.
     103             :         // Stop is safe to call concurrently with run.
     104             :         stop func()
     105             : 
     106             :         // A cleanup func which must be called after
     107             :         // the benchmark has finished running.
     108             :         // Clean should be only called after making sure
     109             :         // that the run function is no longer executing.
     110             :         clean func()
     111             : }
     112             : 
     113             : // createFile can be used to create an empty file.
     114             : // Invariant: File shouldn't already exist.
     115           0 : func createFile(filepath string) vfs.File {
     116           0 :         fh, err := fsConfig.fs.Create(filepath, vfs.WriteCategoryUnspecified)
     117           0 :         if err != nil {
     118           0 :                 log.Fatalln(err)
     119           0 :         }
     120           0 :         return fh
     121             : }
     122             : 
     123             : // Invariant: file with filepath should exist.
     124           0 : func deleteFile(filepath string) {
     125           0 :         err := fsConfig.fs.Remove(filepath)
     126           0 :         if err != nil {
     127           0 :                 log.Fatalln(err)
     128           0 :         }
     129             : }
     130             : 
     131             : // Write size bytes to the file in batches.
     132           0 : func writeToFile(fh vfs.File, size int64) {
     133           0 :         for size > 0 {
     134           0 :                 var toWrite []byte
     135           0 :                 if size >= writeBatchSize {
     136           0 :                         toWrite = fsConfig.precomputedWriteBatch
     137           0 :                 } else {
     138           0 :                         toWrite = fsConfig.precomputedWriteBatch[:size]
     139           0 :                 }
     140           0 :                 written, err := fh.Write(toWrite)
     141           0 :                 if err != nil {
     142           0 :                         log.Fatalln(err)
     143           0 :                 }
     144           0 :                 if written != len(toWrite) {
     145           0 :                         log.Fatalf("Couldn't write %d bytes to file\n", size)
     146           0 :                 }
     147           0 :                 size -= int64(len(toWrite))
     148             :         }
     149             : }
     150             : 
     151           0 : func syncFile(fh vfs.File) {
     152           0 :         err := fh.Sync()
     153           0 :         if err != nil {
     154           0 :                 log.Fatalln(err)
     155           0 :         }
     156             : }
     157             : 
     158           0 : func closeFile(fh vfs.File) {
     159           0 :         err := fh.Close()
     160           0 :         if err != nil {
     161           0 :                 log.Fatalln(err)
     162           0 :         }
     163             : }
     164             : 
     165           0 : func getDiskUsage(filepath string) {
     166           0 :         _, err := fsConfig.fs.GetDiskUsage(filepath)
     167           0 :         if err != nil {
     168           0 :                 log.Fatalln(err)
     169           0 :         }
     170             : }
     171             : 
     172           0 : func openDir(filepath string) vfs.File {
     173           0 :         fh, err := fsConfig.fs.OpenDir(filepath)
     174           0 :         if err != nil {
     175           0 :                 log.Fatalln(err)
     176           0 :         }
     177           0 :         return fh
     178             : }
     179             : 
     180           0 : func mkDir(filepath string) {
     181           0 :         err := fsConfig.fs.MkdirAll(filepath, 0755)
     182           0 :         if err != nil {
     183           0 :                 log.Fatalln(err)
     184           0 :         }
     185             : }
     186             : 
     187           0 : func removeAllFiles(filepath string) {
     188           0 :         err := fsConfig.fs.RemoveAll(filepath)
     189           0 :         if err != nil {
     190           0 :                 log.Fatalln(err)
     191           0 :         }
     192             : }
     193             : 
     194             : // fileSize is in bytes.
     195           1 : func createBench(benchName string, benchDescription string) fsBenchmark {
     196           1 :         createBench := func(dirpath string) *fsBench {
     197           0 :                 bench := &fsBench{}
     198           0 :                 mkDir(dirpath)
     199           0 :                 fh := openDir(dirpath)
     200           0 : 
     201           0 :                 bench.dir = fh
     202           0 :                 bench.dirName = dirpath
     203           0 :                 bench.reg = newHistogramRegistry()
     204           0 :                 bench.numOps = 0
     205           0 :                 bench.name = benchName
     206           0 :                 bench.description = benchDescription
     207           0 : 
     208           0 :                 // setup the operation to benchmark, and the cleanup functions.
     209           0 :                 pref := "temp_"
     210           0 :                 var numFiles int
     211           0 :                 var done atomic.Bool
     212           0 : 
     213           0 :                 bench.run = func(hist *namedHistogram) bool {
     214           0 :                         if done.Load() {
     215           0 :                                 return false
     216           0 :                         }
     217             : 
     218           0 :                         start := time.Now()
     219           0 :                         fh := createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, numFiles)))
     220           0 :                         syncFile(bench.dir)
     221           0 :                         hist.Record(time.Since(start))
     222           0 : 
     223           0 :                         closeFile(fh)
     224           0 :                         numFiles++
     225           0 :                         return true
     226             :                 }
     227             : 
     228           0 :                 bench.stop = func() {
     229           0 :                         done.Store(true)
     230           0 :                 }
     231             : 
     232           0 :                 bench.clean = func() {
     233           0 :                         removeAllFiles(dirpath)
     234           0 :                         closeFile(bench.dir)
     235           0 :                 }
     236             : 
     237           0 :                 return bench
     238             :         }
     239             : 
     240           1 :         return fsBenchmark{
     241           1 :                 createBench,
     242           1 :                 benchName,
     243           1 :                 benchDescription,
     244           1 :         }
     245             : }
     246             : 
     247             : // This benchmark prepopulates a directory with some files of a given size. Then, it creates and deletes
     248             : // a file of some size, while measuring only the performance of the delete.
     249             : func deleteBench(
     250             :         benchName string, benchDescription string, preNumFiles int, preFileSize int64, fileSize int64,
     251           1 : ) fsBenchmark {
     252           1 : 
     253           1 :         createBench := func(dirpath string) *fsBench {
     254           0 :                 bench := &fsBench{}
     255           0 :                 mkDir(dirpath)
     256           0 :                 fh := openDir(dirpath)
     257           0 : 
     258           0 :                 bench.dir = fh
     259           0 :                 bench.dirName = dirpath
     260           0 :                 bench.reg = newHistogramRegistry()
     261           0 :                 bench.numOps = 0
     262           0 :                 bench.name = benchName
     263           0 :                 bench.description = benchDescription
     264           0 : 
     265           0 :                 // prepopulate the directory
     266           0 :                 prePref := "pre_temp_"
     267           0 :                 for i := 0; i < preNumFiles; i++ {
     268           0 :                         fh := createFile(path.Join(dirpath, fmt.Sprintf("%s%d", prePref, i)))
     269           0 :                         if preFileSize > 0 {
     270           0 :                                 writeToFile(fh, preFileSize)
     271           0 :                                 syncFile(fh)
     272           0 :                         }
     273           0 :                         closeFile(fh)
     274             :                 }
     275           0 :                 syncFile(bench.dir)
     276           0 : 
     277           0 :                 var done atomic.Bool
     278           0 :                 bench.run = func(hist *namedHistogram) bool {
     279           0 :                         if done.Load() {
     280           0 :                                 return false
     281           0 :                         }
     282             : 
     283           0 :                         filename := "newfile"
     284           0 :                         fh := createFile(path.Join(dirpath, filename))
     285           0 :                         writeToFile(fh, fileSize)
     286           0 :                         syncFile(fh)
     287           0 : 
     288           0 :                         start := time.Now()
     289           0 :                         deleteFile(path.Join(dirpath, filename))
     290           0 :                         hist.Record(time.Since(start))
     291           0 : 
     292           0 :                         return true
     293             :                 }
     294             : 
     295           0 :                 bench.stop = func() {
     296           0 :                         done.Store(true)
     297           0 :                 }
     298             : 
     299           0 :                 bench.clean = func() {
     300           0 :                         removeAllFiles(dirpath)
     301           0 :                         closeFile(bench.dir)
     302           0 :                 }
     303             : 
     304           0 :                 return bench
     305             :         }
     306             : 
     307           1 :         return fsBenchmark{
     308           1 :                 createBench,
     309           1 :                 benchName,
     310           1 :                 benchDescription,
     311           1 :         }
     312             : }
     313             : 
     314             : // This benchmark creates some files in a directory, and then measures the performance
     315             : // of the vfs.Remove function.
     316             : // fileSize is in bytes.
     317             : func deleteUniformBench(
     318             :         benchName string, benchDescription string, numFiles int, fileSize int64,
     319           1 : ) fsBenchmark {
     320           1 :         createBench := func(dirpath string) *fsBench {
     321           0 :                 bench := &fsBench{}
     322           0 :                 mkDir(dirpath)
     323           0 :                 fh := openDir(dirpath)
     324           0 : 
     325           0 :                 bench.dir = fh
     326           0 :                 bench.dirName = dirpath
     327           0 :                 bench.reg = newHistogramRegistry()
     328           0 :                 bench.numOps = 0
     329           0 :                 bench.name = benchName
     330           0 :                 bench.description = benchDescription
     331           0 : 
     332           0 :                 // setup the operation to benchmark, and the cleaup functions.
     333           0 :                 pref := "temp_"
     334           0 :                 for i := 0; i < numFiles; i++ {
     335           0 :                         fh := createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, i)))
     336           0 :                         if fileSize > 0 {
     337           0 :                                 writeToFile(fh, fileSize)
     338           0 :                                 syncFile(fh)
     339           0 :                         }
     340           0 :                         closeFile(fh)
     341             :                 }
     342           0 :                 syncFile(bench.dir)
     343           0 : 
     344           0 :                 var done atomic.Bool
     345           0 :                 bench.run = func(hist *namedHistogram) bool {
     346           0 :                         if done.Load() {
     347           0 :                                 return false
     348           0 :                         }
     349             : 
     350           0 :                         if numFiles == 0 {
     351           0 :                                 return false
     352           0 :                         }
     353             : 
     354           0 :                         start := time.Now()
     355           0 :                         deleteFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, numFiles-1)))
     356           0 :                         hist.Record(time.Since(start))
     357           0 : 
     358           0 :                         numFiles--
     359           0 :                         return true
     360             :                 }
     361             : 
     362           0 :                 bench.stop = func() {
     363           0 :                         done.Store(true)
     364           0 :                 }
     365             : 
     366           0 :                 bench.clean = func() {
     367           0 :                         removeAll(dirpath)
     368           0 :                         closeFile(bench.dir)
     369           0 :                 }
     370             : 
     371           0 :                 return bench
     372             :         }
     373             : 
     374           1 :         return fsBenchmark{
     375           1 :                 createBench,
     376           1 :                 benchName,
     377           1 :                 benchDescription,
     378           1 :         }
     379             : }
     380             : 
     381             : // Tests the performance of syncing data to disk.
     382             : // Only measures the sync performance.
     383             : // The writes will be synced after every writeSize bytes have been written.
     384             : func writeSyncBench(
     385             :         benchName string, benchDescription string, maxFileSize int64, writeSize int64,
     386           1 : ) fsBenchmark {
     387           1 : 
     388           1 :         if writeSize > maxFileSize {
     389           0 :                 log.Fatalln("File write threshold is greater than max file size.")
     390           0 :         }
     391             : 
     392           1 :         createBench := func(dirpath string) *fsBench {
     393           0 :                 bench := &fsBench{}
     394           0 :                 mkDir(dirpath)
     395           0 :                 fh := openDir(dirpath)
     396           0 : 
     397           0 :                 bench.dir = fh
     398           0 :                 bench.dirName = dirpath
     399           0 :                 bench.reg = newHistogramRegistry()
     400           0 :                 bench.numOps = 0
     401           0 :                 bench.name = benchName
     402           0 :                 bench.description = benchDescription
     403           0 : 
     404           0 :                 pref := "temp_"
     405           0 :                 var benchData struct {
     406           0 :                         done         atomic.Bool
     407           0 :                         fh           vfs.File
     408           0 :                         fileNum      int
     409           0 :                         bytesWritten int64
     410           0 :                 }
     411           0 :                 benchData.fh = createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, benchData.fileNum)))
     412           0 : 
     413           0 :                 bench.run = func(hist *namedHistogram) bool {
     414           0 :                         if benchData.done.Load() {
     415           0 :                                 return false
     416           0 :                         }
     417             : 
     418           0 :                         if benchData.bytesWritten+writeSize > maxFileSize {
     419           0 :                                 closeFile(benchData.fh)
     420           0 :                                 benchData.fileNum++
     421           0 :                                 benchData.bytesWritten = 0
     422           0 :                                 benchData.fh = createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, benchData.fileNum)))
     423           0 :                         }
     424             : 
     425           0 :                         benchData.bytesWritten += writeSize
     426           0 :                         writeToFile(benchData.fh, writeSize)
     427           0 : 
     428           0 :                         start := time.Now()
     429           0 :                         syncFile(benchData.fh)
     430           0 :                         hist.Record(time.Since(start))
     431           0 : 
     432           0 :                         return true
     433             :                 }
     434             : 
     435           0 :                 bench.stop = func() {
     436           0 :                         benchData.done.Store(true)
     437           0 :                 }
     438             : 
     439           0 :                 bench.clean = func() {
     440           0 :                         closeFile(benchData.fh)
     441           0 :                         removeAllFiles(dirpath)
     442           0 :                         closeFile(bench.dir)
     443           0 :                 }
     444             : 
     445           0 :                 return bench
     446             :         }
     447             : 
     448           1 :         return fsBenchmark{
     449           1 :                 createBench,
     450           1 :                 benchName,
     451           1 :                 benchDescription,
     452           1 :         }
     453             : }
     454             : 
     455             : // Tests the peformance of calling the vfs.GetDiskUsage call on a directory,
     456             : // as the number of files/total size of files in the directory grows.
     457             : func diskUsageBench(
     458             :         benchName string, benchDescription string, maxFileSize int64, writeSize int64,
     459           1 : ) fsBenchmark {
     460           1 : 
     461           1 :         if writeSize > maxFileSize {
     462           0 :                 log.Fatalln("File write threshold is greater than max file size.")
     463           0 :         }
     464             : 
     465           1 :         createBench := func(dirpath string) *fsBench {
     466           0 :                 bench := &fsBench{}
     467           0 :                 mkDir(dirpath)
     468           0 :                 fh := openDir(dirpath)
     469           0 : 
     470           0 :                 bench.dir = fh
     471           0 :                 bench.dirName = dirpath
     472           0 :                 bench.reg = newHistogramRegistry()
     473           0 :                 bench.numOps = 0
     474           0 :                 bench.name = benchName
     475           0 :                 bench.description = benchDescription
     476           0 : 
     477           0 :                 pref := "temp_"
     478           0 :                 var benchData struct {
     479           0 :                         done         atomic.Bool
     480           0 :                         fh           vfs.File
     481           0 :                         fileNum      int
     482           0 :                         bytesWritten int64
     483           0 :                 }
     484           0 :                 benchData.fh = createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, benchData.fileNum)))
     485           0 : 
     486           0 :                 bench.run = func(hist *namedHistogram) bool {
     487           0 :                         if benchData.done.Load() {
     488           0 :                                 return false
     489           0 :                         }
     490             : 
     491           0 :                         if benchData.bytesWritten+writeSize > maxFileSize {
     492           0 :                                 closeFile(benchData.fh)
     493           0 :                                 benchData.fileNum++
     494           0 :                                 benchData.bytesWritten = 0
     495           0 :                                 benchData.fh = createFile(path.Join(dirpath, fmt.Sprintf("%s%d", pref, benchData.fileNum)))
     496           0 :                         }
     497             : 
     498           0 :                         benchData.bytesWritten += writeSize
     499           0 :                         writeToFile(benchData.fh, writeSize)
     500           0 :                         syncFile(benchData.fh)
     501           0 : 
     502           0 :                         start := time.Now()
     503           0 :                         getDiskUsage(dirpath)
     504           0 :                         hist.Record(time.Since(start))
     505           0 : 
     506           0 :                         return true
     507             :                 }
     508             : 
     509           0 :                 bench.stop = func() {
     510           0 :                         benchData.done.Store(true)
     511           0 :                 }
     512             : 
     513           0 :                 bench.clean = func() {
     514           0 :                         closeFile(benchData.fh)
     515           0 :                         removeAllFiles(dirpath)
     516           0 :                         closeFile(bench.dir)
     517           0 :                 }
     518             : 
     519           0 :                 return bench
     520             :         }
     521             : 
     522           1 :         return fsBenchmark{
     523           1 :                 createBench,
     524           1 :                 benchName,
     525           1 :                 benchDescription,
     526           1 :         }
     527             : }
     528             : 
     529             : // A benchmark is a function which takes a directory
     530             : // as input and returns the fsBench struct which has
     531             : // all the information required to run the benchmark.
     532             : type fsBenchmark struct {
     533             :         createBench func(string) *fsBench
     534             :         name        string
     535             :         description string
     536             : }
     537             : 
     538             : // The various benchmarks which can be run.
     539             : var benchmarks = map[string]fsBenchmark{
     540             :         "create_empty": createBench("create_empty", "create empty file, sync par dir"),
     541             :         "delete_10k_2MiB": deleteUniformBench(
     542             :                 "delete_10k_2MiB", "create 10k 2MiB size files, measure deletion times", 10_000, 2<<20,
     543             :         ),
     544             :         "delete_100k_2MiB": deleteUniformBench(
     545             :                 "delete_100k_2MiB", "create 100k 2MiB size files, measure deletion times", 100_000, 2<<20,
     546             :         ),
     547             :         "delete_200k_2MiB": deleteUniformBench(
     548             :                 "delete_200k_2MiB", "create 200k 2MiB size files, measure deletion times", 200_000, 2<<20,
     549             :         ),
     550             :         "write_sync_1MiB": writeSyncBench(
     551             :                 "write_sync_1MiB", "Write 1MiB to a file, then sync, while timing the sync.", 2<<30, 1<<20,
     552             :         ),
     553             :         "write_sync_16MiB": writeSyncBench(
     554             :                 "write_sync_16MiB", "Write 16MiB to a file, then sync, while timing the sync.", 2<<30, 16<<20,
     555             :         ),
     556             :         "write_sync_128MiB": writeSyncBench(
     557             :                 "write_sync_128MiB", "Write 128MiB to a file, then sync, while timing the sync.", 2<<30, 128<<20,
     558             :         ),
     559             :         "disk_usage_128MB": diskUsageBench(
     560             :                 "disk_usage_128MB",
     561             :                 "Write 128MiB to a file, measure GetDiskUsage call. Create a new file, when file size is 1GB.",
     562             :                 1<<30, 128<<20,
     563             :         ),
     564             :         "disk_usage_many_files": diskUsageBench(
     565             :                 "disk_usage_many_files",
     566             :                 "Create new file, Write 128KiB to a file, measure GetDiskUsage call.",
     567             :                 128<<10, 128<<10,
     568             :         ),
     569             :         "delete_large_dir_256MiB": deleteBench(
     570             :                 "delete_large_dir_256MiB", "Prepopulate directory with 100k 1MiB files, measure delete peformance of 256MiB files",
     571             :                 1e5, 1<<20, 256<<20,
     572             :         ),
     573             :         "delete_large_dir_2MiB": deleteBench(
     574             :                 "delete_large_dir_2MiB", "Prepopulate directory with 100k 1MiB files, measure delete peformance of 2MiB files",
     575             :                 1e5, 1<<20, 2<<20,
     576             :         ),
     577             :         "delete_small_dir_2GiB": deleteBench(
     578             :                 "delete_small_dir_2GiB", "Prepopulate directory with 1k 1MiB files, measure delete peformance of 2GiB files",
     579             :                 1e3, 1<<20, 2<<30,
     580             :         ),
     581             :         "delete_small_dir_256MiB": deleteBench(
     582             :                 "delete_small_dir_256MiB", "Prepopulate directory with 1k 1MiB files, measure delete peformance of 256MiB files",
     583             :                 1e3, 1<<20, 256<<20,
     584             :         ),
     585             :         "delete_small_dir_2MiB": deleteBench(
     586             :                 "delete_small_dir_2MiB", "Prepopulate directory with 1k 1MiB files, measure delete peformance of 2MiB files",
     587             :                 1e3, 1<<20, 2<<20,
     588             :         ),
     589             : }
     590             : 
     591           0 : func runFsBench(_ *cobra.Command, args []string) error {
     592           0 :         benchmark, ok := benchmarks[fsConfig.benchname]
     593           0 :         if !ok {
     594           0 :                 return errors.Errorf("trying to run an unknown benchmark: %s", fsConfig.benchname)
     595           0 :         }
     596             : 
     597             :         // Run the benchmark a comple of times.
     598           0 :         fmt.Printf("The benchmark will be run %d time(s).\n", fsConfig.numTimes)
     599           0 :         for i := 0; i < fsConfig.numTimes; i++ {
     600           0 :                 fmt.Println("Starting benchmark:", i)
     601           0 :                 benchStruct := benchmark.createBench(args[0])
     602           0 :                 runTestWithoutDB(testWithoutDB{
     603           0 :                         init: benchStruct.init,
     604           0 :                         tick: benchStruct.tick,
     605           0 :                         done: benchStruct.done,
     606           0 :                 })
     607           0 :         }
     608           0 :         return nil
     609             : }
     610             : 
     611           0 : func (bench *fsBench) init(wg *sync.WaitGroup) {
     612           0 :         fmt.Println("Running benchmark:", bench.name)
     613           0 :         fmt.Println("Description:", bench.description)
     614           0 : 
     615           0 :         wg.Add(1)
     616           0 :         go bench.execute(wg)
     617           0 : }
     618             : 
     619           0 : func (bench *fsBench) execute(wg *sync.WaitGroup) {
     620           0 :         defer wg.Done()
     621           0 : 
     622           0 :         latencyHist := bench.reg.Register(bench.name)
     623           0 : 
     624           0 :         for {
     625           0 :                 // run the op which we're benchmarking.
     626           0 :                 bench.numOps++
     627           0 : 
     628           0 :                 // The running function will determine exactly what to latency
     629           0 :                 // it wants to measure.
     630           0 :                 continueBench := bench.run(latencyHist)
     631           0 :                 if !continueBench || (fsConfig.maxOps > 0 && bench.numOps >= fsConfig.maxOps) {
     632           0 :                         break
     633             :                 }
     634             :         }
     635             : }
     636             : 
     637           0 : func (bench *fsBench) tick(elapsed time.Duration, i int) {
     638           0 :         if i%20 == 0 {
     639           0 :                 fmt.Println("____optype__elapsed__ops/sec(inst)___ops/sec(cum)__p50(ms)__p95(ms)__p99(ms)__pMax(ms)")
     640           0 :         }
     641           0 :         bench.reg.Tick(func(tick histogramTick) {
     642           0 :                 h := tick.Hist
     643           0 : 
     644           0 :                 fmt.Printf("%10s %8s %14.1f %14.1f %5.6f %5.6f %5.6f %5.6f\n",
     645           0 :                         tick.Name[:10],
     646           0 :                         time.Duration(elapsed.Seconds()+0.5)*time.Second,
     647           0 :                         float64(h.TotalCount())/tick.Elapsed.Seconds(),
     648           0 :                         float64(tick.Cumulative.TotalCount())/elapsed.Seconds(),
     649           0 :                         time.Duration(h.ValueAtQuantile(50)).Seconds()*1000,
     650           0 :                         time.Duration(h.ValueAtQuantile(95)).Seconds()*1000,
     651           0 :                         time.Duration(h.ValueAtQuantile(99)).Seconds()*1000,
     652           0 :                         time.Duration(h.ValueAtQuantile(100)).Seconds()*1000,
     653           0 :                 )
     654           0 :         })
     655             : }
     656             : 
     657           0 : func (bench *fsBench) done(wg *sync.WaitGroup, elapsed time.Duration) {
     658           0 :         // Do the cleanup.
     659           0 :         bench.stop()
     660           0 :         wg.Wait()
     661           0 :         defer bench.clean()
     662           0 : 
     663           0 :         fmt.Println("\n____optype__elapsed_____ops(total)___ops/sec(cum)__avg(ms)__p50(ms)__p95(ms)__p99(ms)__pMax(ms)")
     664           0 : 
     665           0 :         resultTick := histogramTick{}
     666           0 :         bench.reg.Tick(func(tick histogramTick) {
     667           0 :                 h := tick.Cumulative
     668           0 :                 if resultTick.Cumulative == nil {
     669           0 :                         resultTick.Now = tick.Now
     670           0 :                         resultTick.Cumulative = h
     671           0 :                 } else {
     672           0 :                         resultTick.Cumulative.Merge(h)
     673           0 :                 }
     674             : 
     675           0 :                 fmt.Printf("%10s %7.1fs %14d %14.1f %5.6f %5.6f %5.6f %5.6f %5.6f\n",
     676           0 :                         tick.Name[:10], elapsed.Seconds(), h.TotalCount(),
     677           0 :                         float64(h.TotalCount())/elapsed.Seconds(),
     678           0 :                         time.Duration(h.Mean()).Seconds()*1000,
     679           0 :                         time.Duration(h.ValueAtQuantile(50)).Seconds()*1000,
     680           0 :                         time.Duration(h.ValueAtQuantile(95)).Seconds()*1000,
     681           0 :                         time.Duration(h.ValueAtQuantile(99)).Seconds()*1000,
     682           0 :                         time.Duration(h.ValueAtQuantile(100)).Seconds()*1000,
     683           0 :                 )
     684             :         })
     685           0 :         fmt.Println()
     686           0 : 
     687           0 :         resultHist := resultTick.Cumulative
     688           0 : 
     689           0 :         fmt.Printf("Benchmarkfsbench/%s  %d %0.1f ops/sec\n\n",
     690           0 :                 bench.name,
     691           0 :                 resultHist.TotalCount(),
     692           0 :                 float64(resultHist.TotalCount())/elapsed.Seconds(),
     693           0 :         )
     694             : }
     695             : 
     696           0 : func verbosef(fmtstr string, args ...interface{}) {
     697           0 :         if verbose {
     698           0 :                 fmt.Printf(fmtstr, args...)
     699           0 :         }
     700             : }
     701             : 
     702           0 : func removeAll(dir string) {
     703           0 :         verbosef("Removing %q.\n", dir)
     704           0 :         if err := os.RemoveAll(dir); err != nil {
     705           0 :                 log.Fatal(err)
     706           0 :         }
     707             : }

Generated by: LCOV version 1.14