summaryrefslogtreecommitdiff
path: root/ninja/src/build_log_perftest.cc
blob: 810c06554aa120d6f4327f753aaa3668a2f20249 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>
#include <stdlib.h>

#include "build_log.h"
#include "graph.h"
#include "manifest_parser.h"
#include "state.h"
#include "util.h"
#include "metrics.h"

#ifndef _WIN32
#include <unistd.h>
#endif

const char kTestFilename[] = "BuildLogPerfTest-tempfile";

struct NoDeadPaths : public BuildLogUser {
  virtual bool IsPathDead(StringPiece) const { return false; }
};

bool WriteTestData(string* err) {
  BuildLog log;

  NoDeadPaths no_dead_paths;
  if (!log.OpenForWrite(kTestFilename, no_dead_paths, err))
    return false;

  /*
  A histogram of command lengths in chromium. For example, 407 builds,
  1.4% of all builds, had commands longer than 32 bytes but shorter than 64.
       32    407   1.4%
       64    183   0.6%
      128   1461   5.1%
      256    791   2.8%
      512   1314   4.6%
     1024   6114  21.3%
     2048  11759  41.0%
     4096   2056   7.2%
     8192   4567  15.9%
    16384     13   0.0%
    32768      4   0.0%
    65536      5   0.0%
  The average command length is 4.1 kB and there were 28674 commands in total,
  which makes for a total log size of ~120 MB (also counting output filenames).

  Based on this, write 30000 many 4 kB long command lines.
  */

  // ManifestParser is the only object allowed to create Rules.
  const size_t kRuleSize = 4000;
  string long_rule_command = "gcc ";
  for (int i = 0; long_rule_command.size() < kRuleSize; ++i) {
    char buf[80];
    sprintf(buf, "-I../../and/arbitrary/but/fairly/long/path/suffixed/%d ", i);
    long_rule_command += buf;
  }
  long_rule_command += "$in -o $out\n";

  State state;
  ManifestParser parser(&state, NULL);
  if (!parser.ParseTest("rule cxx\n  command = " + long_rule_command, err))
    return false;

  // Create build edges. Using ManifestParser is as fast as using the State api
  // for edge creation, so just use that.
  const int kNumCommands = 30000;
  string build_rules;
  for (int i = 0; i < kNumCommands; ++i) {
    char buf[80];
    sprintf(buf, "build input%d.o: cxx input%d.cc\n", i, i);
    build_rules += buf;
  }

  if (!parser.ParseTest(build_rules, err))
    return false;

  for (int i = 0; i < kNumCommands; ++i) {
    log.RecordCommand(state.edges_[i],
                      /*start_time=*/100 * i,
                      /*end_time=*/100 * i + 1,
                      /*restat_mtime=*/0);
  }

  return true;
}

int main() {
  vector<int> times;
  string err;

  if (!WriteTestData(&err)) {
    fprintf(stderr, "Failed to write test data: %s\n", err.c_str());
    return 1;
  }

  {
    // Read once to warm up disk cache.
    BuildLog log;
    if (!log.Load(kTestFilename, &err)) {
      fprintf(stderr, "Failed to read test data: %s\n", err.c_str());
      return 1;
    }
  }
  const int kNumRepetitions = 5;
  for (int i = 0; i < kNumRepetitions; ++i) {
    int64_t start = GetTimeMillis();
    BuildLog log;
    if (!log.Load(kTestFilename, &err)) {
      fprintf(stderr, "Failed to read test data: %s\n", err.c_str());
      return 1;
    }
    int delta = (int)(GetTimeMillis() - start);
    printf("%dms\n", delta);
    times.push_back(delta);
  }

  int min = times[0];
  int max = times[0];
  float total = 0;
  for (size_t i = 0; i < times.size(); ++i) {
    total += times[i];
    if (times[i] < min)
      min = times[i];
    else if (times[i] > max)
      max = times[i];
  }

  printf("min %dms  max %dms  avg %.1fms\n",
         min, max, total / times.size());

  unlink(kTestFilename);

  return 0;
}