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 }