github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/cmd/build_log_perftest/build_log_perftest.go (about) 1 // Copyright 2012 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "fmt" 19 "os" 20 "time" 21 22 "github.com/maruel/nin" 23 ) 24 25 const testFilename = "BuildLogPerfTest-tempfile" 26 27 type noDeadPaths struct { 28 } 29 30 func (n *noDeadPaths) IsPathDead(string) bool { 31 return false 32 } 33 34 func writeTestData() error { 35 log := nin.NewBuildLog() 36 if err := log.OpenForWrite(testFilename, &noDeadPaths{}); err != nil { 37 return err 38 } 39 40 /* 41 A histogram of command lengths in chromium. For example, 407 builds, 42 1.4% of all builds, had commands longer than 32 bytes but shorter than 64. 43 32 407 1.4% 44 64 183 0.6% 45 128 1461 5.1% 46 256 791 2.8% 47 512 1314 4.6% 48 1024 6114 21.3% 49 2048 11759 41.0% 50 4096 2056 7.2% 51 8192 4567 15.9% 52 16384 13 0.0% 53 32768 4 0.0% 54 65536 5 0.0% 55 The average command length is 4.1 kB and there were 28674 commands in total, 56 which makes for a total log size of ~120 MB (also counting output filenames). 57 58 Based on this, write 30000 many 4 kB long command lines. 59 */ 60 61 // ParseManifest() is the only function allowed to create Rules. 62 kRuleSize := 4000 63 longRuleCommand := "gcc " 64 for i := 0; len(longRuleCommand) < kRuleSize; i++ { 65 longRuleCommand += fmt.Sprintf("-I../../and/arbitrary/but/fairly/long/path/suffixed/%d ", i) 66 } 67 longRuleCommand += "$in -o $out\n" 68 69 state := nin.NewState() 70 opts := nin.ParseManifestOpts{Quiet: true} 71 input := []byte("rule cxx\n command = " + longRuleCommand + "\x00") 72 if err := nin.ParseManifest(&state, nil, opts, "input", input); err != nil { 73 return err 74 } 75 76 // Create build edges. Using ParseManifest() is as fast as using the State 77 // api for edge creation, so just use that. 78 kNumCommands := int32(30000) 79 buildRules := "" 80 for i := int32(0); i < kNumCommands; i++ { 81 buildRules += fmt.Sprintf("build input%d.o: cxx input%d.cc\n", i, i) 82 } 83 84 input = []byte(buildRules + "\x00") 85 if err := nin.ParseManifest(&state, nil, opts, "input", input); err != nil { 86 return err 87 } 88 89 for i := int32(0); i < kNumCommands; i++ { 90 if err := log.RecordCommand(state.Edges[i] /*startTime=*/, 100*i /*endTime=*/, 100*i+1 /*mtime=*/, 0); err != nil { 91 return err 92 } 93 } 94 95 return nil 96 } 97 98 func mainImpl() error { 99 if err := writeTestData(); err != nil { 100 return fmt.Errorf("failed to write test data: %w", err) 101 } 102 103 { 104 // Read once to warm up disk cache. 105 log := nin.NewBuildLog() 106 if s, err := log.Load(testFilename); s == nin.LoadError { 107 return fmt.Errorf("failed to read test data: %s", err) 108 } 109 } 110 111 rnd := time.Microsecond 112 var times []time.Duration 113 kNumRepetitions := 5 114 for i := 0; i < kNumRepetitions; i++ { 115 start := time.Now() 116 log := nin.NewBuildLog() 117 if s, err := log.Load(testFilename); s == nin.LoadError { 118 return fmt.Errorf("failed to read test data: %s", err) 119 } 120 delta := time.Since(start) 121 fmt.Printf("%s\n", delta.Round(rnd)) 122 times = append(times, delta) 123 } 124 125 min := times[0] 126 max := times[0] 127 total := time.Duration(0) 128 for i := 0; i < len(times); i++ { 129 total += times[i] 130 if times[i] < min { 131 min = times[i] 132 } else if times[i] > max { 133 max = times[i] 134 } 135 } 136 avg := total / time.Duration(len(times)) 137 fmt.Printf("min %s max %s avg %s\n", min.Round(rnd), max.Round(rnd), avg.Round(rnd)) 138 return os.Remove(testFilename) 139 } 140 141 func main() { 142 if err := mainImpl(); err != nil { 143 fmt.Fprintf(os.Stderr, "build_log_perftest: %s\n", err) 144 os.Exit(1) 145 } 146 }