OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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:])) |
OLD | NEW |