40
40
#
41
41
import argparse
42
42
import contextlib
43
- import fnmatch
43
+ import shlex
44
44
from argparse import ArgumentParser
45
45
from enum import Enum
46
46
from typing import List , Set , Tuple , NamedTuple
@@ -69,9 +69,9 @@ def parser_help_message(cls):
69
69
return (
70
70
"additional benchmark arguments. By default, arguments are passed to the Polybench launcher, "
71
71
f"but you may use flags to forward arguments to specific components: "
72
- f"{ cls .MX_BENCHMARK_FLAG } for mx benchmark, "
73
- f"{ cls .VM_FLAG } for the host VM or native-image, or "
74
- f"{ cls .POLYBENCH_FLAG } for the Polybench launcher. "
72
+ f' "{ cls .MX_BENCHMARK_FLAG } " for mx benchmark, '
73
+ f' "{ cls .VM_FLAG } " for the host VM or native-image, or '
74
+ f' "{ cls .POLYBENCH_FLAG } " for the Polybench launcher (pass "--help" to see launcher help). '
75
75
f'For example: "-i 2 { cls .MX_BENCHMARK_FLAG } --fail-fast { cls .VM_FLAG } -ea '
76
76
f'{ cls .POLYBENCH_FLAG } --engine.TraceCompilation=true".'
77
77
)
@@ -124,57 +124,60 @@ def append(self, other: "PolybenchArgumentsSpecification") -> "PolybenchArgument
124
124
)
125
125
126
126
127
- def polybench_list (args ):
127
+ def polybench_list ():
128
128
benchmarks = _resolve_all_benchmarks ()
129
- print ('Benchmark files (run using "mx polybench run <glob_pattern>"):' )
129
+ mx .log ("Listing all available polybench benchmarks." )
130
+ mx .log ('Benchmark files (run using "mx polybench <glob_pattern>"):' )
130
131
file_found = False
131
132
for benchmark_name , resolved_benchmark in benchmarks .items ():
132
- if args .glob and not fnmatch .fnmatchcase (benchmark_name , f"*{ args .glob } *" ):
133
- continue
134
133
file_found = True
135
- print (f"\t { benchmark_name } " )
136
- if args .verbose :
137
- print (f"\t \t absolute path: { resolved_benchmark .absolute_path } " )
138
- print (f"\t \t distribution: { resolved_benchmark .suite .benchmark_distribution } " )
139
- print (f"\t \t declaring suite: { resolved_benchmark .suite .mx_suite .name } " )
140
- print (f"\t \t required languages: { resolved_benchmark .suite .languages } " )
134
+ mx .log (f"\t { benchmark_name } " )
135
+ mx .logv (f"\t \t absolute path: { resolved_benchmark .absolute_path } " )
136
+ mx .logv (f"\t \t distribution: { resolved_benchmark .suite .benchmark_distribution } " )
137
+ mx .logv (f"\t \t declaring suite: { resolved_benchmark .suite .mx_suite .name } " )
138
+ mx .logv (f"\t \t required languages: { resolved_benchmark .suite .languages } " )
141
139
if not file_found :
142
- print ("\t no benchmark files found" )
140
+ mx . log ("\t no benchmark files found" )
143
141
144
142
suites = _get_all_suites ()
145
- print (
146
- 'Suites (run using "mx polybench run --suite <suite_name>" or "mx polybench run --suite <suite_name>:<tag1>,<tag2>,..."):'
143
+ mx . log (
144
+ 'Suites (run using "mx polybench --suite <suite_name>" or "mx polybench --suite <suite_name>:<tag1>,<tag2>,..."):'
147
145
)
148
146
suite_found = False
149
147
for suite_name , suite in suites .items ():
150
- if args .glob and not fnmatch .fnmatchcase (suite_name , f"*{ args .glob } *" ):
151
- continue
152
148
suite_found = True
153
- print (f"\t { suite_name } : { suite .tags if suite .tags else '{}' } " )
149
+ mx . log (f"\t { suite_name } : { suite .tags if suite .tags else '{}' } " )
154
150
if not suite_found :
155
- print ("\t no suites found" )
151
+ mx . log ("\t no suites found" )
156
152
157
153
158
- def polybench_run (args ):
159
- if args .suite :
160
- _run_suite (args )
154
+ def polybench_run (parsed_args , raw_args ):
155
+ if parsed_args .suite :
156
+ _run_suite (parsed_args )
157
+ elif parsed_args .dry_run_polybench :
158
+ _dry_run_polybench (raw_args )
161
159
else :
162
- _run_benchmark_pattern (args )
160
+ _run_benchmark_pattern (parsed_args )
161
+
162
+
163
+ def _dry_run_polybench (raw_args ):
164
+ raw_args .remove ("--dry-run-polybench" )
165
+ mx .log (_mx_polybench_command_string (raw_args ))
163
166
164
167
165
168
def _run_suite (args ):
166
169
suite , tags = _get_suite_and_tags (args )
167
170
if tags - suite .tags :
168
171
mx .abort (
169
- f" Requested tag(s) not available for suite ' { suite } ' : { tags - suite .tags } . Available tags: { suite .tags } "
172
+ f' Requested tag(s) not available for suite " { suite } " : { tags - suite .tags } . Available tags: { suite .tags } '
170
173
)
171
174
172
175
mx .log (f"Running suite { suite .name } with tags { tags } ." )
173
176
174
177
# Arguments passed in a suite run are appended after the arguments specified by the suite runner.
175
- if args .arguments :
178
+ if args .benchmark_arguments :
176
179
mx .warn (
177
- f"Arguments were supplied on the command line ({ args .arguments } ). "
180
+ f"Arguments were supplied on the command line ({ args .benchmark_arguments } ). "
178
181
"These arguments will be inserted after any arguments supplied by the suite runner."
179
182
)
180
183
features = _get_vm_features (args )
@@ -184,21 +187,24 @@ def _run_suite(args):
184
187
"You can directly enable these features in the suite runner itself."
185
188
)
186
189
override_arguments = (
187
- PolybenchArgumentsSpecification .parse (args .arguments )
190
+ PolybenchArgumentsSpecification .parse (args .benchmark_arguments )
188
191
.append (
189
192
# Ensure results are combined across multiple runs.
190
193
PolybenchArgumentsSpecification (mx_benchmark_args = ["--append-results" ])
191
194
)
192
195
.to_normalized_command_line ()
193
196
)
194
197
195
- base_args = ["run" ]
198
+ base_args = []
196
199
if args .dry_run :
197
200
base_args .append ("--dry-run" )
201
+ elif args .dry_run_polybench :
202
+ base_args .append ("--dry-run-polybench" )
198
203
199
204
def polybench_run_function (argument_list : List [str ]) -> None :
200
- parsed_args = parser .parse_args (base_args + argument_list + override_arguments )
201
- polybench_run (parsed_args )
205
+ raw_args = base_args + argument_list + override_arguments
206
+ parsed_args = parser .parse_args (raw_args )
207
+ polybench_run (parsed_args , raw_args )
202
208
203
209
with _run_suite_context (suite , tags ):
204
210
suite .runner (polybench_run_function , tags )
@@ -248,7 +254,7 @@ def require_native(feature_name):
248
254
249
255
250
256
def _run_benchmark_pattern (args ):
251
- arguments_spec = PolybenchArgumentsSpecification .parse (args .arguments )
257
+ arguments_spec = PolybenchArgumentsSpecification .parse (args .benchmark_arguments )
252
258
run_spec = PolybenchRunSpecification (args .benchmarks , _get_vm_features (args ), arguments_spec )
253
259
_validate_jdk (run_spec .is_native ())
254
260
mx .logv (f"Performing polybench run: { run_spec } " )
@@ -282,7 +288,7 @@ def _parse_mx_benchmark_pattern(pattern: str, pattern_is_glob: bool) -> str:
282
288
if pattern .count (":" ) == 1 :
283
289
message += ' This pattern looks like a suite. Did you forget "--suite"?'
284
290
else :
285
- message += ' Use "mx polybench run -h" to view the expected format for benchmark patterns.'
291
+ message += ' Use "mx polybench -h" to view the expected format for benchmark patterns.'
286
292
mx .abort (message )
287
293
288
294
if pattern_is_glob :
@@ -374,79 +380,121 @@ def _run_specification(spec: PolybenchRunSpecification, pattern_is_glob: bool =
374
380
mx_benchmark .benchmark (mx_benchmark_args )
375
381
376
382
377
- def _mx_benchmark_command_string ( mx_benchmark_args : List [ str ] ) -> str :
378
- command = f "mx --java-home { mx .get_jdk ().home } "
383
+ def _base_mx_command ( ) -> List [ str ] :
384
+ command = [ "mx" , " --java-home" , mx .get_jdk ().home ]
379
385
for dynamic_import , in_subdir in mx .get_dynamic_imports ():
380
386
if in_subdir :
381
- command += f" --dy /{ dynamic_import } "
387
+ command += [ " --dy" , f" /{ dynamic_import } "]
382
388
else :
383
- command += f" --dy { dynamic_import } "
384
- command += " benchmark "
385
- command += " " .join (mx_benchmark_args )
389
+ command += ["--dy" , dynamic_import ]
386
390
return command
387
391
388
392
393
+ def _mx_polybench_command_string (mx_polybench_args : List [str ]) -> str :
394
+ return _build_command (_base_mx_command () + ["polybench" ] + mx_polybench_args )
395
+
396
+
397
+ def _mx_benchmark_command_string (mx_benchmark_args : List [str ]) -> str :
398
+ return _build_command (_base_mx_command () + ["benchmark" ] + mx_benchmark_args )
399
+
400
+
401
+ def _build_command (command : List [str ]) -> str :
402
+ return " " .join (shlex .quote (token ) for token in command )
403
+
404
+
389
405
def _create_parser () -> ArgumentParser :
390
406
parser = ArgumentParser (
391
407
prog = "mx polybench" ,
392
408
description = (
393
409
"mx polybench is a simple command line interface for Polybench, the system used to benchmark Truffle languages. "
394
- "It is a thin wrapper around Polybench's mx benchmark integration that makes it easy to discover benchmarks "
395
- '(using "mx polybench list") and to run them (using "mx polybench run"). '
410
+ "It is a thin wrapper around Polybench's mx benchmark integration that makes it easy to discover and run benchmarks. "
396
411
"It also supports batch execution of the benchmarks in a suite, which is convenient for defining CI jobs."
397
412
),
413
+ usage = "%(prog)s [options*] benchmarks [benchmark_arguments*]" ,
398
414
)
399
- subparsers = parser .add_subparsers (dest = "command" , required = True , help = "the polybench command to run" )
400
-
401
- list_parser = subparsers .add_parser ("list" , help = "list the available benchmarks" )
402
- list_parser .add_argument ("glob" , nargs = "?" , type = str , help = "a glob pattern to filter benchmark output" )
403
- list_parser .add_argument ("--verbose" , action = "store_true" , help = "print a detailed view of the benchmarks" )
404
- list_parser .set_defaults (func = polybench_list )
405
415
406
- run_parser = subparsers .add_parser ("run" , help = "run one or more benchmarks" )
407
- run_parser .add_argument (
416
+ parser .add_argument (
408
417
"benchmarks" ,
418
+ nargs = "?" ,
409
419
help = 'a glob pattern representing benchmarks to run (e.g., "interpreter/*"), '
410
420
'or a suite specification if "--suite" is provided. '
411
- 'Use "mx polybench list" to see a list of available benchmarks.' ,
421
+ 'Use "-- list" to see a list of available benchmarks.' ,
412
422
)
413
- run_parser .add_argument (
414
- "arguments " , nargs = argparse . REMAINDER , help = PolybenchArgumentsSpecification . parser_help_message ()
423
+ parser .add_argument (
424
+ "--list " , action = "store_true" , help = "list all available benchmarks (without actually executing them)."
415
425
)
416
- run_parser .add_argument (
417
- "--dry-run" ,
426
+
427
+ run_flags = set ()
428
+
429
+ def run_flag (run_arg ):
430
+ run_flags .add (run_arg )
431
+ return run_arg
432
+
433
+ dry_run_group = parser .add_mutually_exclusive_group ()
434
+ dry_run_group .add_argument (
435
+ run_flag ("--dry-run" ),
418
436
action = "store_true" ,
419
437
help = "log the mx benchmark commands that would be executed by this command (without actually executing them)" ,
420
438
)
421
- mode_group = run_parser .add_mutually_exclusive_group ()
439
+ dry_run_group .add_argument (
440
+ run_flag ("--dry-run-polybench" ),
441
+ action = "store_true" ,
442
+ help = "log the mx polybench commands that would be executed by this command (without actually executing them)" ,
443
+ )
444
+
445
+ mode_group = parser .add_mutually_exclusive_group ()
422
446
mode_group .add_argument (
423
- "--jvm" , action = "store_false" , dest = "is_native" , default = False , help = "run the benchmark on the JVM (default)"
447
+ run_flag ("--jvm" ),
448
+ action = "store_false" ,
449
+ dest = "is_native" ,
450
+ default = False ,
451
+ help = "run the benchmark on the JVM (default)" ,
424
452
)
425
453
mode_group .add_argument (
426
- "--native" ,
454
+ run_flag ( "--native" ) ,
427
455
action = "store_true" ,
428
456
dest = "is_native" ,
429
457
help = "run the benchmark with a native image of the Truffle interpreter" ,
430
458
)
431
- run_parser .add_argument ("--pgo" , action = "store_true" , default = False , help = "use PGO (only valid for native runs)" )
432
- run_parser .add_argument ("--g1gc" , action = "store_true" , default = False , help = "use G1GC (only valid for native runs)" )
433
- benchmark_pattern_group = run_parser .add_mutually_exclusive_group ()
459
+ parser .add_argument (
460
+ run_flag ("--pgo" ), action = "store_true" , default = False , help = "use PGO (only valid for native runs)"
461
+ )
462
+ parser .add_argument (
463
+ run_flag ("--g1gc" ), action = "store_true" , default = False , help = "use G1GC (only valid for native runs)"
464
+ )
465
+ benchmark_pattern_group = parser .add_mutually_exclusive_group ()
434
466
benchmark_pattern_group .add_argument (
435
- "--suite" ,
467
+ run_flag ( "--suite" ) ,
436
468
action = "store_true" ,
437
469
help = 'treat "benchmarks" as a suite specification (a suite name, optionally '
438
470
"followed by a colon and a comma-separated list of tags, "
439
471
'e.g., "sl:gate,daily")' ,
440
472
)
441
473
benchmark_pattern_group .add_argument (
442
- "--use-mx-benchmark-pattern" ,
474
+ run_flag ( "--use-mx-benchmark-pattern" ) ,
443
475
action = "store_false" ,
444
476
dest = "pattern_is_glob" ,
445
477
default = True ,
446
478
help = 'treat "benchmarks" as an mx benchmark pattern instead of a glob '
447
479
'(e.g., "r[interpreter/.*]"; see mx_benchmark.py for details)' ,
448
480
)
449
- run_parser .set_defaults (func = polybench_run )
481
+
482
+ def benchmark_argument (arg ):
483
+ """Prevents users from accidentally passing a run flag in the benchmark arguments section."""
484
+ if arg in run_flags :
485
+ mx .abort (
486
+ f'Flag "{ arg } " is an "mx polybench" flag, but it was specified in the extra benchmark arguments section.\n '
487
+ f'Move it before the benchmark pattern/suite specification to fix this error (e.g., "mx polybench { arg } ... benchmark_or_suite ...").'
488
+ )
489
+ return arg
490
+
491
+ parser .add_argument (
492
+ "benchmark_arguments" ,
493
+ nargs = argparse .REMAINDER ,
494
+ type = benchmark_argument ,
495
+ help = PolybenchArgumentsSpecification .parser_help_message (),
496
+ )
497
+
450
498
return parser
451
499
452
500
@@ -457,5 +505,10 @@ def _create_parser() -> ArgumentParser:
457
505
def polybench_command (args ):
458
506
"""Run one or more benchmarks using polybench."""
459
507
parsed_args = parser .parse_args (args )
460
- mx .logv (f"Running polybench command { parsed_args .command } with arguments { parsed_args } " )
461
- parsed_args .func (parsed_args )
508
+ mx .logv (f"Running polybench with arguments { parsed_args } " )
509
+ if parsed_args .list :
510
+ polybench_list ()
511
+ elif parsed_args .benchmarks :
512
+ polybench_run (parsed_args , args )
513
+ else :
514
+ mx .abort ('A benchmark pattern or --list must be specified. Use "mx polybench -h" for more info.' )
0 commit comments