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  }