github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/cmd/manifest_parser_perftest/manifest_parser_perftest.go (about) 1 // Copyright 2014 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 // Tests manifest parser performance. Expects to be run in ninja's root 16 // directory. 17 package main 18 19 import ( 20 "errors" 21 "flag" 22 "fmt" 23 "os" 24 "os/exec" 25 "path/filepath" 26 "time" 27 28 "github.com/maruel/nin" 29 ) 30 31 func writeFakeManifests(dir string) error { 32 if _, err := os.Stat(filepath.Join(dir, "build.ninja")); err == nil { 33 fmt.Printf("Creating manifest data... [SKIP]\n") 34 return nil 35 } 36 fmt.Printf("Creating manifest data...") 37 cmd := exec.Command("python3", filepath.Join("misc", "write_fake_manifests.py"), dir) 38 if err := cmd.Run(); err != nil { 39 return err 40 } 41 fmt.Printf("done.\n") 42 return nil 43 } 44 45 func loadManifests(measureCommandEvaluation bool) int { 46 di := nin.RealDiskInterface{} 47 input, err := di.ReadFile("build.ninja") 48 if err != nil { 49 fmt.Fprintf(os.Stderr, "Failed to read test data: %s\n", err) 50 os.Exit(1) 51 } 52 state := nin.NewState() 53 if err := nin.ParseManifest(&state, &di, nin.ParseManifestOpts{}, "build.ninja", input); err != nil { 54 fmt.Fprintf(os.Stderr, "Failed to parse test data: %s\n", err) 55 os.Exit(1) 56 } 57 // Doing an empty build involves reading the manifest and evaluating all 58 // commands required for the requested targets. So include command 59 // evaluation in the perftest by default. 60 optimizationGuard := 0 61 if measureCommandEvaluation { 62 for _, e := range state.Edges { 63 optimizationGuard += len(e.EvaluateCommand(false)) 64 } 65 } 66 return optimizationGuard 67 } 68 69 func mainImpl() error { 70 f := flag.Bool("f", false, "only measure manifest load time, not command evaluation time") 71 flag.Parse() 72 if len(flag.Args()) != 0 { 73 return errors.New("unexpected arguments") 74 } 75 76 // Disable __pycache__. 77 if err := os.Setenv("PYTHONDONTWRITEBYTECODE", "x"); err != nil { 78 return err 79 } 80 81 kManifestDir := filepath.Join("build", "manifest_perftest") 82 83 if err := writeFakeManifests(kManifestDir); err != nil { 84 return fmt.Errorf("failed to write test data: %s", err) 85 } 86 87 if err := os.Chdir(kManifestDir); err != nil { 88 return err 89 } 90 91 rnd := time.Microsecond 92 kNumRepetitions := 5 93 var times []time.Duration 94 for i := 0; i < kNumRepetitions; i++ { 95 start := time.Now() 96 optimizationGuard := loadManifests(!*f) 97 delta := time.Since(start) 98 fmt.Printf("%s (hash: %x)\n", delta.Round(rnd), optimizationGuard) 99 times = append(times, delta) 100 } 101 102 min := times[0] 103 max := times[0] 104 total := times[0] 105 for i := 1; i < len(times); i++ { 106 if min > times[i] { 107 min = times[i] 108 } 109 if max < times[i] { 110 max = times[i] 111 } 112 total += times[i] 113 } 114 avg := total / time.Duration(len(times)) 115 fmt.Printf("min %s max %s avg %s\n", min.Round(rnd), max.Round(rnd), avg.Round(rnd)) 116 return nil 117 } 118 119 func main() { 120 if err := mainImpl(); err != nil { 121 fmt.Fprintf(os.Stderr, "manifest_parser_perftest: %s\n", err) 122 os.Exit(1) 123 } 124 }