25from __future__
import print_function
35from argparse
import ArgumentParser
36from multiprocessing
import Process
37from threading
import Thread
38from threading
import Lock
39from pdb
import set_trace
43\033[31mPlease call calmbench.py to drive this script if you're not doing so.
44This script is not supposed to be used by itself. (At least, it's not easy to
45use by itself. The calmbench bots may use this script directly.)
54UNITS =
"ns µs ms s".split()
63 parser = ArgumentParser(description=HELP)
65 parser.add_argument(
'outdir', type=str, help=
"output directory")
66 parser.add_argument(
'a', type=str, help=
"name of A")
67 parser.add_argument(
'b', type=str, help=
"name of B")
68 parser.add_argument(
'nano_a', type=str, help=
"path to A's nanobench binary")
69 parser.add_argument(
'nano_b', type=str, help=
"path to B's nanobench binary")
70 parser.add_argument(
'arg_a', type=str, help=
"args for A's nanobench run")
71 parser.add_argument(
'arg_b', type=str, help=
"args for B's nanobench run")
72 parser.add_argument(
'repeat', type=int, help=
"number of initial runs")
73 parser.add_argument(
'skip_b', type=str, help=(
"whether to skip running B"
74 " ('true' or 'false')"))
75 parser.add_argument(
'config', type=str, help=
"nanobenh config")
76 parser.add_argument(
'threads', type=int, help=
"number of threads to run")
77 parser.add_argument(
'noinit', type=str, help=(
"whether to skip running B"
78 " ('true' or 'false')"))
80 parser.add_argument(
'--concise', dest=
'concise', action=
"store_true",
81 help=
"If set, no verbose thread info will be printed.")
82 parser.set_defaults(concise=
False)
85 BHELP =
"bot specific options"
86 parser.add_argument(
'--githash', type=str, default=
"", help=BHELP)
87 parser.add_argument(
'--keys', type=str, default=[], nargs=
'+', help=BHELP)
89 args = parser.parse_args()
90 args.skip_b = args.skip_b ==
"true"
91 args.noinit = args.noinit ==
"true"
93 if args.threads == -1:
95 if args.config
in [
"8888",
"565"]:
96 args.threads =
max(1, multiprocessing.cpu_count() / 2)
101 if key
not in dict_array:
103 dict_array[key].
append(value)
104 dict_array[key].
sort()
108 normalized_t = t * 1000 ** UNITS.index(unit);
109 if name.startswith(args.a):
116 with open(filename)
as f:
117 lines = f.readlines()
122 matches = re.search(
"([+-]?\d*.?\d+)(s|ms|µs|ns)", items[3])
123 if (
not matches
or items[9] != args.config):
125 time_num = matches.group(1)
126 time_unit = matches.group(2)
132 super(ThreadWithException, self).
__init__(target = target)
137 self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
138 except BaseException
as e:
142 super(ThreadWithException, self).
join(timeout)
146 """Simplest and stupidiest threaded executer."""
162 spinners = [
". ",
".. ",
"..."]
166 "\r" + spinners[i %
len(spinners)] +
175 ts =
Thread(target = spin);
184 exceptions.append(t.exception)
192 for exc
in exceptions:
198 raw = shlex.split(arg)
202 result.append(os.path.expanduser(r))
208def run(args, threadRunner, name, nano, arg, i):
210 file_i =
"%s/%s.out%d" % (args.outdir, name, i)
212 should_run =
not args.noinit
and not (name == args.b
and args.skip_b)
219 print(
"Init run %d for %s..." % (i, name))
221 subprocess.check_call([
"touch", file_i])
222 with open(file_i,
'w')
as f:
223 subprocess.check_call([nano] +
split_arg(arg) +
224 [
"--config", args.config], stderr=f, stdout=f)
230 threadRunner.add(args, task)
235 for i
in range(1,
max(args.repeat, args.threads / 2) + 1):
236 run(args, threadRunner, args.a, args.nano_a, args.arg_a, i)
237 run(args, threadRunner, args.b, args.nano_b, args.arg_b, i)
242 i =
max(0, (
len(values) - 1) / FACTOR)
243 return values[i], values[-i - 1]
247 return upper2 < DIFF_T * lower1
255 for bench
in timesA.keys():
256 if bench
not in timesB:
261 suspects.append(bench)
267 return "^\"" + s[0:(s.index(
".skp") + 3)] +
"\""
269 return "^\"" + s +
"\"$"
273 patterns =
map(process_bench_pattern, suspects)
274 return " --match " + (
" ".
join(patterns))
278 return array[
len(array) / 2]
308 last_unchanged_iter = 0
309 last_suspect_number = -1
312 while tryCnt < MAXTRY:
315 if len(suspects) != last_suspect_number:
316 last_suspect_number =
len(suspects)
317 last_unchanged_iter = it
318 if (
len(suspects) == 0
or it - last_unchanged_iter >= TERM):
321 print(
"Number of suspects at iteration %d: %d" % (it,
len(suspects)))
323 for j
in range(1,
max(1, args.threads / 2) + 1):
324 run(args, threadRunner, args.a, args.nano_a,
326 run(args, threadRunner, args.b, args.nano_b,
332 if len(suspects) == 0:
333 print((
"%s and %s does not seem to have significant " + \
334 "performance differences.") % (args.a, args.b))
336 suspects.sort(key = regression)
337 print(
"%s (compared to %s) is likely" % (args.a, args.b))
338 for suspect
in suspects:
341 print(
"\033[31m %s slower in %s\033[0m" % (
format_r(1/r), suspect))
343 print(
"\033[32m %s faster in %s\033[0m" % (
format_r(r), suspect))
345 with open(
"%s/bench_%s_%s.json" % (args.outdir, args.a, args.b),
'w')
as f:
348 r =
regression(bench)
if bench
in suspects
else 1.0
365 output = {
"results": results}
367 output[
"gitHash"] = args.githash
370 for i
in range(
len(args.keys) / 2):
371 keys[args.keys[i * 2]] = args.keys[i * 2 + 1]
373 f.write(json.dumps(output, indent=4))
374 print((
"\033[36mJSON results available in %s\033[0m" % f.name))
376 with open(
"%s/bench_%s_%s.csv" % (args.outdir, args.a, args.b),
'w')
as out:
377 out.write((
"bench, significant?, raw regresion, " +
378 "%(A)s quantile (ns), %(B)s quantile (ns), " +
379 "%(A)s (ns), %(B)s (ns)\n") % {
'A': args.a,
'B': args.b})
380 for bench
in suspects + timesA.keys():
381 if (bench
not in timesA
or bench
not in timesB):
386 "%s, %s, %f, " % (bench, bench
in suspects,
regression(bench)) +
392 "Compared %d benches. " +
393 "%d of them seem to be significantly differrent." +
395 (
len([x
for x
in timesA
if x
in timesB]),
len(suspects)))
396 print(
"\033[36mPlease see detailed bench results in %s\033[0m" % out.name)
399if __name__ ==
"__main__":
402 except Exception
as e:
405 traceback.print_exc()
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
def __init__(self, target)
def join(self, timeout=None)
static void append(char **dst, size_t *count, const char *src, size_t n)
static float max(float r, float g, float b)
def append_dict_sorted_array(dict_array, key, value)
def suspects_arg(suspects)
def get_lower_upper(values)
def different_enough(lower1, upper2)
def run(args, threadRunner, name, nano, arg, i)
def append_times_from_file(args, name, filename)
def add_time(args, name, bench, t, unit)
def process_bench_pattern(s)
def print(*args, **kwargs)
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
static SkString join(const CommandLineFlags::StringArray &)
static sk_sp< SkColorFilter > spin(sk_sp< SkColorFilter > cf)