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 : }
|