github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/test/benchmark_test.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  // These benchmarks can be run with:
     6  // go test -test.bench=. -benchmem
     7  // go test -test.bench=. -benchmem -tags fuse
     8  // go test -test.bench=. -benchmem -tags dokan
     9  
    10  package test
    11  
    12  import (
    13  	"fmt"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/keybase/client/go/kbfs/data"
    18  )
    19  
    20  // BenchmarkWriteSeq512 writes to a large file in 512 byte writes.
    21  func BenchmarkWriteSeq512(b *testing.B) {
    22  	benchmarkWriteSeqN(b, 512, 0xFFFFFFFFFFFF)
    23  }
    24  func BenchmarkWriteSeq4k(b *testing.B) {
    25  	benchmarkWriteSeqN(b, 4*1024, 0xFFFFFFFFFFFF)
    26  }
    27  func BenchmarkWriteSeq64k(b *testing.B) {
    28  	benchmarkWriteSeqN(b, 64*1024, 0xFFFFFFFFFFFF)
    29  }
    30  func BenchmarkWriteSeq512k(b *testing.B) {
    31  	benchmarkWriteSeqN(b, 512*1024, 0xFFFFFFFFFFFF)
    32  }
    33  
    34  // BenchmarkWrite1mb512 writes to a 1mb file in 512 byte writes.
    35  func BenchmarkWrite1mb512(b *testing.B) {
    36  	benchmarkWriteSeqN(b, 512, 0xFFFFF)
    37  }
    38  func BenchmarkWrite1mb4k(b *testing.B) {
    39  	benchmarkWriteSeqN(b, 4*1024, 0xFFFFF)
    40  }
    41  func BenchmarkWrite1mb64k(b *testing.B) {
    42  	benchmarkWriteSeqN(b, 64*1024, 0xFFFFF)
    43  }
    44  func BenchmarkWrite1mb512k(b *testing.B) {
    45  	benchmarkWriteSeqN(b, 512*1024, 0xFFFFF)
    46  }
    47  
    48  func benchmarkWriteSeqN(b *testing.B, n int64, mask int64) {
    49  	buf := make([]byte, n)
    50  	b.SetBytes(n)
    51  	benchmark(b,
    52  		users("alice"),
    53  		as(alice,
    54  			custom(func(cb func(fileOp) error) error {
    55  				err := cb(mkfile("bench", ""))
    56  				if err != nil {
    57  					return err
    58  				}
    59  				var n int
    60  				err = cb(getBenchN(&n))
    61  				if err != nil {
    62  					return err
    63  				}
    64  				err = cb(resetTimer())
    65  				if err != nil {
    66  					return err
    67  				}
    68  				for i := 0; i < n; i++ {
    69  					err = cb(pwriteBS("bench", buf, (int64(i*n))&mask))
    70  					if err != nil {
    71  						return err
    72  					}
    73  				}
    74  				return cb(stopTimer())
    75  			}),
    76  		),
    77  	)
    78  }
    79  
    80  // BenchmarkReadHoleSeq512 reads from a large file in 512 byte reads.
    81  func BenchmarkReadHoleSeq512(b *testing.B) {
    82  	benchmarkReadSeqHoleN(b, 512, 0xFFFFFFF)
    83  }
    84  func BenchmarkReadHoleSeq4k(b *testing.B) {
    85  	benchmarkReadSeqHoleN(b, 4*1024, 0xFFFFFFF)
    86  }
    87  func BenchmarkReadHoleSeq64k(b *testing.B) {
    88  	benchmarkReadSeqHoleN(b, 64*1024, 0xFFFFFFF)
    89  }
    90  func BenchmarkReadHoleSeq512k(b *testing.B) {
    91  	benchmarkReadSeqHoleN(b, 512*1024, 0xFFFFFFF)
    92  }
    93  
    94  // BenchmarkReadHole1mb512 reads from a 1mb file in 512 byte reads.
    95  func BenchmarkReadHole1mb512(b *testing.B) {
    96  	benchmarkReadSeqHoleN(b, 512, 0xFFFFF)
    97  }
    98  func BenchmarkReadHole1mb4k(b *testing.B) {
    99  	benchmarkReadSeqHoleN(b, 4*1024, 0xFFFFF)
   100  }
   101  func BenchmarkReadHole1mb64k(b *testing.B) {
   102  	benchmarkReadSeqHoleN(b, 64*1024, 0xFFFFF)
   103  }
   104  func BenchmarkReadHole1mb512k(b *testing.B) {
   105  	benchmarkReadSeqHoleN(b, 512*1024, 0xFFFFF)
   106  }
   107  
   108  func benchmarkReadSeqHoleN(b *testing.B, n int64, mask int64) {
   109  	buf := make([]byte, n)
   110  	b.SetBytes(n)
   111  	benchmark(b,
   112  		users("alice"),
   113  		as(alice,
   114  			custom(func(cb func(fileOp) error) error {
   115  				err := cb(mkfile("bench", ""))
   116  				if err != nil {
   117  					return err
   118  				}
   119  				err = cb(truncate("bench", uint64(mask+1)))
   120  				if err != nil {
   121  					return err
   122  				}
   123  				var n int
   124  				err = cb(getBenchN(&n))
   125  				if err != nil {
   126  					return err
   127  				}
   128  				err = cb(resetTimer())
   129  				if err != nil {
   130  					return err
   131  				}
   132  				for i := 0; i < n; i++ {
   133  					err = cb(preadBS("bench", buf, (int64(i*n))&mask))
   134  					if err != nil {
   135  						return err
   136  					}
   137  				}
   138  				return cb(stopTimer())
   139  			}),
   140  		),
   141  	)
   142  }
   143  
   144  func benchmarkDoBenchWrites(b *testing.B, cb func(fileOp) error,
   145  	numWritesPerFile int, buf []byte, startIter int) error {
   146  	var n int
   147  	err := cb(getBenchN(&n))
   148  	if err != nil {
   149  		return err
   150  	}
   151  	for i := startIter; i < n+startIter; i++ {
   152  		name := fmt.Sprintf("bench%d", i)
   153  		err := cb(mkfile(name, ""))
   154  		if err != nil {
   155  			return err
   156  		}
   157  		for j := 0; j < numWritesPerFile; j++ {
   158  			// make each block unique
   159  			for k := 0; k < 1+len(buf)/data.MaxBlockSizeBytesDefault; k++ {
   160  				buf[k] = byte(i)
   161  				buf[k+1] = byte(j)
   162  				buf[k+2] = byte(k)
   163  			}
   164  			// Only sync after the last write
   165  			sync := j+1 == numWritesPerFile
   166  			err = cb(pwriteBSSync(name, buf,
   167  				int64(j)*int64(len(buf)), sync))
   168  			if err != nil {
   169  				return err
   170  			}
   171  		}
   172  	}
   173  	return nil
   174  }
   175  
   176  func benchmarkWriteWithBandwidthHelper(b *testing.B, fileBytes int64,
   177  	perWriteBytes int64, writebwKBps int, doWarmUp bool) {
   178  	buf := make([]byte, perWriteBytes)
   179  	b.SetBytes(fileBytes)
   180  	numWritesPerFile := int(fileBytes / perWriteBytes)
   181  	benchmark(b,
   182  		users("alice"),
   183  		blockSize(512<<10),
   184  		bandwidth(writebwKBps),
   185  		opTimeout(19*time.Second),
   186  		as(alice,
   187  			custom(func(cb func(fileOp) error) (err error) {
   188  				startIter := 0
   189  				var n int
   190  				err = cb(getBenchN(&n))
   191  				if err != nil {
   192  					return err
   193  				}
   194  				if doWarmUp {
   195  					if err := benchmarkDoBenchWrites(b, cb,
   196  						numWritesPerFile, buf, 0); err != nil {
   197  						return err
   198  					}
   199  					startIter = n
   200  				}
   201  				err = cb(resetTimer())
   202  				if err != nil {
   203  					return err
   204  				}
   205  				defer func() {
   206  					stopErr := cb(stopTimer())
   207  					if err == nil && stopErr != nil {
   208  						err = stopErr
   209  					}
   210  				}()
   211  				return benchmarkDoBenchWrites(b, cb, numWritesPerFile, buf,
   212  					startIter)
   213  			}),
   214  		),
   215  	)
   216  }
   217  
   218  func benchmarkWriteWithBandwidthPlusWarmup(b *testing.B, fileBytes int64,
   219  	perWriteBytes int64, writebwKBps int) {
   220  	benchmarkWriteWithBandwidthHelper(b, fileBytes, perWriteBytes,
   221  		writebwKBps, true)
   222  }
   223  
   224  func benchmarkWriteWithBandwidth(b *testing.B, fileBytes int64,
   225  	perWriteBytes int64, writebwKBps int) {
   226  	benchmarkWriteWithBandwidthHelper(b, fileBytes, perWriteBytes,
   227  		writebwKBps, false)
   228  }
   229  
   230  func BenchmarkWriteMediumFileLowBandwidth(b *testing.B) {
   231  	benchmarkWriteWithBandwidth(b, 10<<20 /* 10 MB */, 1<<16, /* 65 KB writes */
   232  		100 /* 100 KBps */)
   233  }
   234  
   235  func BenchmarkWriteBigFileNormalBandwidth(b *testing.B) {
   236  	// Warm up to get the buffer as large as possible
   237  	benchmarkWriteWithBandwidthPlusWarmup(b, 100<<20, /* 100 MB */
   238  		1<<16 /* 65 KB writes */, 11*1024/8 /* 11 Mbps */)
   239  }
   240  
   241  func BenchmarkWriteBigFileBigBandwidth(b *testing.B) {
   242  	// Warm up to get the buffer as large as possible
   243  	benchmarkWriteWithBandwidthPlusWarmup(b, 1<<30, /* 1 GB */
   244  		1<<20 /*1 MB writes */, 100*1024/8 /* 100 Mbps */)
   245  }
   246  
   247  func BenchmarkWriteMixedFilesNormalBandwidth(b *testing.B) {
   248  	perWriteBytes := int64(1 << 16) // 65 KB writes
   249  	buf := make([]byte, perWriteBytes)
   250  
   251  	// Files:
   252  	// * 100 MB to set the buffer size appropriately
   253  	// * A bunch of 2 MB files
   254  	// * Another 100 MB to make sure the buffer is still sized right
   255  	fileSizes := []int64{100 << 20}
   256  	for i := 0; i < 50; i++ {
   257  		fileSizes = append(fileSizes, 2<<20)
   258  	}
   259  	fileSizes = append(fileSizes, 100<<20)
   260  	var totalSize int64
   261  	for _, size := range fileSizes {
   262  		totalSize += size
   263  	}
   264  	b.SetBytes(totalSize)
   265  
   266  	benchmark(b,
   267  		users("alice"),
   268  		blockSize(512<<10),
   269  		bandwidth(11*1024/8 /* 11 Mbps */),
   270  		opTimeout(19*time.Second),
   271  		as(alice,
   272  			custom(func(cb func(fileOp) error) (err error) {
   273  				var n int
   274  				err = cb(getBenchN(&n))
   275  				if err != nil {
   276  					return err
   277  				}
   278  				err = cb(resetTimer())
   279  				if err != nil {
   280  					return err
   281  				}
   282  				defer func() {
   283  					stopErr := cb(stopTimer())
   284  					if err == nil && stopErr != nil {
   285  						err = stopErr
   286  					}
   287  				}()
   288  				currIter := 0
   289  				for _, fileSize := range fileSizes {
   290  					numWrites := int(fileSize / perWriteBytes)
   291  					if err := benchmarkDoBenchWrites(b, cb,
   292  						numWrites, buf, currIter); err != nil {
   293  						return err
   294  					}
   295  					currIter += n
   296  				}
   297  				return nil
   298  			}),
   299  		),
   300  	)
   301  }
   302  
   303  func benchmarkMultiFileSync(
   304  	b *testing.B, numFiles, fileSize int, timeWrites, timeFlush bool) {
   305  	isolateStages := !timeWrites || !timeFlush
   306  	benchmark(b,
   307  		journal(),
   308  		users("alice"),
   309  		batchSize(20),
   310  		as(alice,
   311  			mkdir("a"),
   312  		),
   313  		as(alice,
   314  			enableJournal(),
   315  			custom(func(cb func(fileOp) error) (err error) {
   316  				if isolateStages {
   317  					// If we want to time one of the stages
   318  					// separately, pause the journal.
   319  					err = cb(pauseJournal())
   320  					if err != nil {
   321  						return err
   322  					}
   323  				}
   324  
   325  				var n int
   326  				err = cb(getBenchN(&n))
   327  				if err != nil {
   328  					return err
   329  				}
   330  				buf := make([]byte, numFiles*fileSize+fileSize)
   331  				for i := 0; i < numFiles*fileSize; i++ {
   332  					// Make sure we mix up the byte values a bit, so
   333  					// we don't accidentally trigger any deduplication.
   334  					buf[i] = byte(i)
   335  				}
   336  				err = cb(resetTimer())
   337  				if err != nil {
   338  					return err
   339  				}
   340  				defer func() {
   341  					stopErr := cb(stopTimer())
   342  					if err == nil && stopErr != nil {
   343  						err = stopErr
   344  					}
   345  				}()
   346  				for iter := 0; iter < n; iter++ {
   347  					if !timeWrites {
   348  						err = cb(stopTimer())
   349  						if err != nil {
   350  							return err
   351  						}
   352  					}
   353  					// Write to each file without syncing.
   354  					for i := iter * numFiles; i < (iter+1)*numFiles; i++ {
   355  						f := fmt.Sprintf("a/b/c/file%d", i)
   356  						start := (i%numFiles)*fileSize + (iter % fileSize)
   357  						err := cb(pwriteBSSync(
   358  							f, buf[start:start+fileSize], 0, false))
   359  						if err != nil {
   360  							return err
   361  						}
   362  					}
   363  					// Sync each file by doing a no-op truncate.
   364  					for i := iter * numFiles; i < (iter+1)*numFiles; i++ {
   365  						f := fmt.Sprintf("a/b/c/file%d", i)
   366  						err := cb(truncate(f, uint64(fileSize)))
   367  						if err != nil {
   368  							return err
   369  						}
   370  					}
   371  					if !timeWrites {
   372  						err = cb(startTimer())
   373  						if err != nil {
   374  							return err
   375  						}
   376  					}
   377  					if !timeFlush {
   378  						err = cb(stopTimer())
   379  						if err != nil {
   380  							return err
   381  						}
   382  					}
   383  					if isolateStages {
   384  						err = cb(resumeJournal())
   385  						if err != nil {
   386  							return err
   387  						}
   388  					}
   389  					err = cb(flushJournal())
   390  					if err != nil {
   391  						return err
   392  					}
   393  					if isolateStages {
   394  						err = cb(pauseJournal())
   395  						if err != nil {
   396  							return err
   397  						}
   398  					}
   399  					if !timeFlush {
   400  						err = cb(startTimer())
   401  						if err != nil {
   402  							return err
   403  						}
   404  					}
   405  				}
   406  				return nil
   407  			}),
   408  		),
   409  	)
   410  }
   411  
   412  func BenchmarkMultiFileSync(b *testing.B) {
   413  	benchmarkMultiFileSync(b, 20, 5, true, false)
   414  }
   415  
   416  func BenchmarkMultiFileSyncLargeWrites(b *testing.B) {
   417  	benchmarkMultiFileSync(b, 1000, 5, true, false)
   418  }
   419  
   420  func BenchmarkMultiFileSyncLargeFlush(b *testing.B) {
   421  	benchmarkMultiFileSync(b, 1000, 5, false, true)
   422  }
   423  
   424  func BenchmarkMultiFileSyncLarge(b *testing.B) {
   425  	benchmarkMultiFileSync(b, 1000, 5, true, true)
   426  }
   427  
   428  func BenchmarkMultiFileSyncBigFilesWrites(b *testing.B) {
   429  	benchmarkMultiFileSync(b, 5, 1*1024*1024, true, false)
   430  }
   431  
   432  func BenchmarkMultiFileSyncBigFilesFlush(b *testing.B) {
   433  	benchmarkMultiFileSync(b, 5, 1*1024*1024, false, true)
   434  }
   435  
   436  func BenchmarkMultiFileSyncBigFiles(b *testing.B) {
   437  	benchmarkMultiFileSync(b, 5, 1*1024*1024, true, true)
   438  }