Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: tools/run_perf.py

Issue 1681283004: [tools] add --pretty switch to run_perf.py Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: better error handling Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 the V8 project authors. All rights reserved. 2 # Copyright 2014 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 Performance runner for d8. 7 Performance runner for d8.
8 8
9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json 9 Call e.g. with tools/run-perf.py --arch ia32 some_suite.json
10 10
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 try: 201 try:
202 if self.stddev_regexp and self.stddev: 202 if self.stddev_regexp and self.stddev:
203 self.errors.append("Test %s should only run once since a stddev " 203 self.errors.append("Test %s should only run once since a stddev "
204 "is provided by the test." % self.name) 204 "is provided by the test." % self.name)
205 if self.stddev_regexp: 205 if self.stddev_regexp:
206 self.stddev = re.search(self.stddev_regexp, stdout, re.M).group(1) 206 self.stddev = re.search(self.stddev_regexp, stdout, re.M).group(1)
207 except: 207 except:
208 self.errors.append("Regexp \"%s\" didn't match for test %s." 208 self.errors.append("Regexp \"%s\" didn't match for test %s."
209 % (self.stddev_regexp, self.name)) 209 % (self.stddev_regexp, self.name))
210 210
211 def GetDeviation(self):
212 if len(self.results) == 0:
213 return 0
214 mean = self.GetMean()
215 square_deviation = sum((float(x)-mean)**2 for x in self.results)
216 return (square_deviation / len(self.results)) ** 0.5
217
218 def GetMean(self):
219 if len(self.results) == 0:
220 return 0
221 return sum(float(x) for x in self.results) / len(self.results)
Michael Achenbach 2016/02/11 09:44:07 nit: sum(self.results) / float(len(self.results))
Camillo Bruni 2016/02/11 10:22:57 I think I got some strings in the results
222
211 def GetResults(self): 223 def GetResults(self):
212 return Results([{ 224 return Results([{
213 "graphs": self.graphs, 225 "graphs": self.graphs,
214 "units": self.units, 226 "units": self.units,
215 "results": self.results, 227 "results": self.results,
216 "stddev": self.stddev, 228 "average": self.GetMean(),
229 "stddev": self.GetDeviation(),
Michael Achenbach 2016/02/11 09:44:07 There are benchmarks that provide their own stddev
Camillo Bruni 2016/02/11 10:22:56 ups... I wanted to return the existing value in Ge
217 }], self.errors) 230 }], self.errors)
218 231
219 232
220 class NullMeasurement(object): 233 class NullMeasurement(object):
221 """Null object to avoid having extra logic for configurations that didn't 234 """Null object to avoid having extra logic for configurations that didn't
222 run like running without patch on trybots. 235 run like running without patch on trybots.
223 """ 236 """
224 def ConsumeOutput(self, stdout): 237 def ConsumeOutput(self, stdout):
225 pass 238 pass
226 239
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 self.timeout = suite.get("timeout_%s" % arch, self.timeout) 411 self.timeout = suite.get("timeout_%s" % arch, self.timeout)
399 self.units = suite.get("units", parent.units) 412 self.units = suite.get("units", parent.units)
400 self.total = suite.get("total", parent.total) 413 self.total = suite.get("total", parent.total)
401 414
402 # A regular expression for results. If the parent graph provides a 415 # A regular expression for results. If the parent graph provides a
403 # regexp and the current suite has none, a string place holder for the 416 # regexp and the current suite has none, a string place holder for the
404 # suite name is expected. 417 # suite name is expected.
405 # TODO(machenbach): Currently that makes only sense for the leaf level. 418 # TODO(machenbach): Currently that makes only sense for the leaf level.
406 # Multiple place holders for multiple levels are not supported. 419 # Multiple place holders for multiple levels are not supported.
407 if parent.results_regexp: 420 if parent.results_regexp:
408 regexp_default = parent.results_regexp % re.escape(suite["name"]) 421 try:
422 regexp_default = parent.results_regexp % re.escape(suite["name"])
423 except TypeError:
424 regexp_default = parent.results_regexp
409 else: 425 else:
410 regexp_default = None 426 regexp_default = None
411 self.results_regexp = suite.get("results_regexp", regexp_default) 427 self.results_regexp = suite.get("results_regexp", regexp_default)
412 428
413 # A similar regular expression for the standard deviation (optional). 429 # A similar regular expression for the standard deviation (optional).
414 if parent.stddev_regexp: 430 if parent.stddev_regexp:
415 stddev_default = parent.stddev_regexp % re.escape(suite["name"]) 431 stddev_default = parent.stddev_regexp % re.escape(suite["name"])
416 else: 432 else:
417 stddev_default = None 433 stddev_default = None
418 self.stddev_regexp = suite.get("stddev_regexp", stddev_default) 434 self.stddev_regexp = suite.get("stddev_regexp", stddev_default)
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
580 self.shell_dir_no_patch = options.shell_dir_no_patch 596 self.shell_dir_no_patch = options.shell_dir_no_patch
581 self.extra_flags = options.extra_flags.split() 597 self.extra_flags = options.extra_flags.split()
582 598
583 @staticmethod 599 @staticmethod
584 def GetPlatform(options): 600 def GetPlatform(options):
585 if options.android_build_tools: 601 if options.android_build_tools:
586 return AndroidPlatform(options) 602 return AndroidPlatform(options)
587 else: 603 else:
588 return DesktopPlatform(options) 604 return DesktopPlatform(options)
589 605
606 def GetPrettyFormatted(self, options):
607 return self
608
590 def _Run(self, runnable, count, no_patch=False): 609 def _Run(self, runnable, count, no_patch=False):
591 raise NotImplementedError() # pragma: no cover 610 raise NotImplementedError() # pragma: no cover
592 611
593 def Run(self, runnable, count): 612 def Run(self, runnable, count):
594 """Execute the benchmark's main file. 613 """Execute the benchmark's main file.
595 614
596 If options.shell_dir_no_patch is specified, the benchmark is run once with 615 If options.shell_dir_no_patch is specified, the benchmark is run once with
597 and once without patch. 616 and once without patch.
598 Args: 617 Args:
599 runnable: A Runnable benchmark instance. 618 runnable: A Runnable benchmark instance.
600 count: The number of this (repeated) run. 619 count: The number of this (repeated) run.
601 Returns: A tuple with the benchmark outputs with and without patch. The 620 Returns: A tuple with the benchmark outputs with and without patch. The
602 latter will be None if options.shell_dir_no_patch was not 621 latter will be None if options.shell_dir_no_patch was not
603 specified. 622 specified.
604 """ 623 """
605 stdout = self._Run(runnable, count, no_patch=False) 624 stdout = self._Run(runnable, count, no_patch=False)
606 if self.shell_dir_no_patch: 625 if self.shell_dir_no_patch:
607 return stdout, self._Run(runnable, count, no_patch=True) 626 return stdout, self._Run(runnable, count, no_patch=True)
608 else: 627 else:
609 return stdout, None 628 return stdout, None
610 629
611 630
612 class DesktopPlatform(Platform): 631 class DesktopPlatform(Platform):
613 def __init__(self, options): 632 def __init__(self, options):
614 super(DesktopPlatform, self).__init__(options) 633 super(DesktopPlatform, self).__init__(options)
615 634
635 def GetPrettyFormatted(self, options):
636 return PrettyFormattedDesktopPlatform(options)
637
616 def PreExecution(self): 638 def PreExecution(self):
617 pass 639 pass
618 640
619 def PostExecution(self): 641 def PostExecution(self):
620 pass 642 pass
621 643
622 def PreTests(self, node, path): 644 def PreTests(self, node, path):
623 if isinstance(node, RunnableConfig): 645 if isinstance(node, RunnableConfig):
624 node.ChangeCWD(path) 646 node.ChangeCWD(path)
625 647
648 def _PrintStdout(self, title, output):
649 print title % "Stdout"
650 print output.stdout
651
626 def _Run(self, runnable, count, no_patch=False): 652 def _Run(self, runnable, count, no_patch=False):
627 suffix = ' - without patch' if no_patch else '' 653 suffix = ' - without patch' if no_patch else ''
628 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir 654 shell_dir = self.shell_dir_no_patch if no_patch else self.shell_dir
629 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix) 655 title = ">>> %%s (#%d)%s:" % ((count + 1), suffix)
630 try: 656 try:
631 output = commands.Execute( 657 output = commands.Execute(
632 runnable.GetCommand(shell_dir, self.extra_flags), 658 runnable.GetCommand(shell_dir, self.extra_flags),
633 timeout=runnable.timeout, 659 timeout=runnable.timeout,
634 ) 660 )
635 except OSError as e: # pragma: no cover 661 except OSError as e: # pragma: no cover
636 print title % "OSError" 662 print title % "OSError"
637 print e 663 print e
638 return "" 664 return ""
639 print title % "Stdout" 665 self._PrintStdout(title, output)
640 print output.stdout
641 if output.stderr: # pragma: no cover 666 if output.stderr: # pragma: no cover
642 # Print stderr for debugging. 667 # Print stderr for debugging.
643 print title % "Stderr" 668 print title % "Stderr"
644 print output.stderr 669 print output.stderr
645 if output.timed_out: 670 if output.timed_out:
646 print ">>> Test timed out after %ss." % runnable.timeout 671 print ">>> Test timed out after %ss." % runnable.timeout
647 if '--prof' in self.extra_flags: 672 if '--prof' in self.extra_flags:
648 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS()) 673 os_prefix = {"linux": "linux", "macos": "mac"}.get(utils.GuessOS())
649 if os_prefix: 674 if os_prefix:
650 tick_tools = os.path.join(TOOLS_BASE, "%s-tick-processor" % os_prefix) 675 tick_tools = os.path.join(TOOLS_BASE, "%s-tick-processor" % os_prefix)
651 subprocess.check_call(tick_tools + " --only-summary", shell=True) 676 subprocess.check_call(tick_tools + " --only-summary", shell=True)
652 else: # pragma: no cover 677 else: # pragma: no cover
653 print "Profiler option currently supported on Linux and Mac OS." 678 print "Profiler option currently supported on Linux and Mac OS."
654 return output.stdout 679 return output.stdout
655 680
656 681
682 class PrettyFormattedDesktopPlatform(DesktopPlatform):
683
684 def _PrintStdout(self, title, output):
685 sys.stdout.write("\r")
686 if output.exit_code != 0:
687 print output.stdout
688 return
689 # Assume the time is on the last line
690 result_line = output.stdout.splitlines()[-1].strip()
691 sys.stdout.write(result_line)
692 # Fill with spaces up to 80 characters.
693 sys.stdout.write(' '*max(0, 80-len(result_line)))
694 sys.stdout.flush()
695
696
657 class AndroidPlatform(Platform): # pragma: no cover 697 class AndroidPlatform(Platform): # pragma: no cover
658 DEVICE_DIR = "/data/local/tmp/v8/" 698 DEVICE_DIR = "/data/local/tmp/v8/"
659 699
660 def __init__(self, options): 700 def __init__(self, options):
661 super(AndroidPlatform, self).__init__(options) 701 super(AndroidPlatform, self).__init__(options)
662 LoadAndroidBuildTools(options.android_build_tools) 702 LoadAndroidBuildTools(options.android_build_tools)
663 703
664 if not options.device: 704 if not options.device:
665 # Detect attached device if not specified. 705 # Detect attached device if not specified.
666 devices = adb_wrapper.AdbWrapper.Devices() 706 devices = adb_wrapper.AdbWrapper.Devices()
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
811 default="") 851 default="")
812 parser.add_option("--json-test-results", 852 parser.add_option("--json-test-results",
813 help="Path to a file for storing json results.") 853 help="Path to a file for storing json results.")
814 parser.add_option("--json-test-results-no-patch", 854 parser.add_option("--json-test-results-no-patch",
815 help="Path to a file for storing json results from run " 855 help="Path to a file for storing json results from run "
816 "without patch.") 856 "without patch.")
817 parser.add_option("--outdir", help="Base directory with compile output", 857 parser.add_option("--outdir", help="Base directory with compile output",
818 default="out") 858 default="out")
819 parser.add_option("--outdir-no-patch", 859 parser.add_option("--outdir-no-patch",
820 help="Base directory with compile output without patch") 860 help="Base directory with compile output without patch")
861 parser.add_option("--pretty",
862 help="Print human readable output",
863 default=False, action="store_true")
821 parser.add_option("--binary-override-path", 864 parser.add_option("--binary-override-path",
822 help="JavaScript engine binary. By default, d8 under " 865 help="JavaScript engine binary. By default, d8 under "
823 "architecture-specific build dir. " 866 "architecture-specific build dir. "
824 "Not supported in conjunction with outdir-no-patch.") 867 "Not supported in conjunction with outdir-no-patch.")
825 868
826 (options, args) = parser.parse_args(args) 869 (options, args) = parser.parse_args(args)
827 870
828 if len(args) == 0: # pragma: no cover 871 if len(args) == 0: # pragma: no cover
829 parser.print_help() 872 parser.print_help()
830 return 1 873 return 1
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 options.shell_dir = os.path.dirname(options.binary_override_path) 909 options.shell_dir = os.path.dirname(options.binary_override_path)
867 default_binary_name = os.path.basename(options.binary_override_path) 910 default_binary_name = os.path.basename(options.binary_override_path)
868 911
869 if options.outdir_no_patch: 912 if options.outdir_no_patch:
870 options.shell_dir_no_patch = os.path.join( 913 options.shell_dir_no_patch = os.path.join(
871 workspace, options.outdir_no_patch, build_config) 914 workspace, options.outdir_no_patch, build_config)
872 else: 915 else:
873 options.shell_dir_no_patch = None 916 options.shell_dir_no_patch = None
874 917
875 platform = Platform.GetPlatform(options) 918 platform = Platform.GetPlatform(options)
919 if options.pretty:
920 platform = platform.GetPrettyFormatted(options)
876 921
877 results = Results() 922 results = Results()
878 results_no_patch = Results() 923 results_no_patch = Results()
879 for path in args: 924 for path in args:
880 path = os.path.abspath(path) 925 path = os.path.abspath(path)
881 926
882 if not os.path.exists(path): # pragma: no cover 927 if not os.path.exists(path): # pragma: no cover
883 results.errors.append("Configuration file %s does not exist." % path) 928 results.errors.append("Configuration file %s does not exist." % path)
884 continue 929 continue
885 930
(...skipping 21 matching lines...) Expand all
907 def Runner(): 952 def Runner():
908 """Output generator that reruns several times.""" 953 """Output generator that reruns several times."""
909 for i in xrange(0, max(1, runnable.run_count)): 954 for i in xrange(0, max(1, runnable.run_count)):
910 # TODO(machenbach): Allow timeout per arch like with run_count per 955 # TODO(machenbach): Allow timeout per arch like with run_count per
911 # arch. 956 # arch.
912 yield platform.Run(runnable, i) 957 yield platform.Run(runnable, i)
913 958
914 # Let runnable iterate over all runs and handle output. 959 # Let runnable iterate over all runs and handle output.
915 result, result_no_patch = runnable.Run( 960 result, result_no_patch = runnable.Run(
916 Runner, trybot=options.shell_dir_no_patch) 961 Runner, trybot=options.shell_dir_no_patch)
962 if options.pretty:
Michael Achenbach 2016/02/11 09:44:07 Maybe factor out this piece of code as printing is
Camillo Bruni 2016/02/11 10:22:57 Yes, I want to have direct feedback. I'll put it i
963 if result.errors:
964 print "\r:Errors:"
965 print "\n".join(set(result.errors))
966 else:
967 trace = result.traces[0]
968 average = trace['average']
969 stdev = trace['stddev']
970 stdev_percentage = 100 * stdev / average if average != 0 else 0
971 print "\r %s +/- %3.2f%% %s" % (average, stdev_percentage, trace['u nits'])
972 sys.stdout.flush()
917 results += result 973 results += result
918 results_no_patch += result_no_patch 974 results_no_patch += result_no_patch
919 platform.PostExecution() 975 platform.PostExecution()
920 976
921 if options.json_test_results: 977 if options.json_test_results:
922 results.WriteToFile(options.json_test_results) 978 results.WriteToFile(options.json_test_results)
923 else: # pragma: no cover 979 else: # pragma: no cover
924 print results 980 if not options.pretty:
981 print results
925 982
926 if options.json_test_results_no_patch: 983 if options.json_test_results_no_patch:
927 results_no_patch.WriteToFile(options.json_test_results_no_patch) 984 results_no_patch.WriteToFile(options.json_test_results_no_patch)
928 else: # pragma: no cover 985 else: # pragma: no cover
929 print results_no_patch 986 if not options.pretty:
987 print results_no_patch
930 988
931 return min(1, len(results.errors)) 989 return min(1, len(results.errors))
932 990
933 if __name__ == "__main__": # pragma: no cover 991 if __name__ == "__main__": # pragma: no cover
934 sys.exit(Main(sys.argv[1:])) 992 sys.exit(Main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698