[Piglit] [PATCH 2/3] framework: allow specifying the number of jobs for concurrency

Dylan Baker dylan at pnwbakers.com
Wed May 30 21:43:02 UTC 2018


Thanks for the changes,

Reviewed-by: Dylan Baker <dylan at pnwbakers.com>

Quoting Marek Olšák (2018-05-30 14:18:15)
> From: Nicolai Hähnle <nicolai.haehnle at amd.com>
> 
> The default remains the same: number of CPUs. But on systems with lots of
> cores but comparatively little (V)RAM it can make sense to reduce the
> number of jobs to avoid random failures caused by out-of-memory conditions.
> ---
>  framework/options.py      |  1 +
>  framework/profile.py      |  5 +++--
>  framework/programs/run.py | 23 +++++++++++++++++++++--
>  3 files changed, 25 insertions(+), 4 deletions(-)
> 
> diff --git a/framework/options.py b/framework/options.py
> index 211159a45..f5f32af78 100644
> --- a/framework/options.py
> +++ b/framework/options.py
> @@ -51,20 +51,21 @@ class _Options(object):  # pylint: disable=too-many-instance-attributes
>      env -- environment variables set for each test before run
>      deqp_mustpass -- True to enable the use of the deqp mustpass list feature.
>      """
>  
>      def __init__(self):
>          self.execute = True
>          self.valgrind = False
>          self.sync = False
>          self.deqp_mustpass = False
>          self.process_isolation = True
> +        self.jobs = None
>  
>          # env is used to set some base environment variables that are not going
>          # to change across runs, without sending them to os.environ which is
>          # fickle and easy to break
>          self.env = {
>              'PIGLIT_SOURCE_DIR':
>                  os.environ.get(
>                      'PIGLIT_SOURCE_DIR',
>                      os.path.abspath(os.path.join(os.path.dirname(__file__),
>                                                   '..')))
> diff --git a/framework/profile.py b/framework/profile.py
> index ffc91e0a6..a6cac2cf0 100644
> --- a/framework/profile.py
> +++ b/framework/profile.py
> @@ -590,37 +590,38 @@ def load_test_profile(filename, python=None):
>                  filename))
>  
>      try:
>          return mod.profile
>      except AttributeError:
>          raise exceptions.PiglitFatalError(
>              'There is no "profile" attribute in module {}.\n'
>              'Did you specify the right file?'.format(filename))
>  
>  
> -def run(profiles, logger, backend, concurrency):
> +def run(profiles, logger, backend, concurrency, jobs):
>      """Runs all tests using Thread pool.
>  
>      When called this method will flatten out self.tests into self.test_list,
>      then will prepare a logger, and begin executing tests through it's Thread
>      pools.
>  
>      Based on the value of concurrency it will either run all the tests
>      concurrently, all serially, or first the thread safe tests then the
>      serial tests.
>  
>      Finally it will print a final summary of the tests.
>  
>      Arguments:
>      profiles -- a list of Profile instances.
>      logger   -- a log.LogManager instance.
>      backend  -- a results.Backend derived instance.
> +    jobs     -- maximum number of concurrent jobs. Use os.cpu_count() by default
>      """
>      chunksize = 1
>  
>      profiles = [(p, p.itertests()) for p in profiles]
>      log = LogManager(logger, sum(len(p) for p, _ in profiles))
>  
>      # check that after the filters are run there are actually tests to run.
>      # if not any(l for _, l in profiles):
>          # raise exceptions.PiglitUserError('no matching tests')
>  
> @@ -663,21 +664,21 @@ def run(profiles, logger, backend, concurrency):
>              # pool
>              run_threads(single, profile, test_list[1],
>                          lambda x: not x[1].run_concurrent)
>          profile.teardown()
>  
>      # Multiprocessing.dummy is a wrapper around Threading that provides a
>      # multiprocessing compatible API
>      #
>      # The default value of pool is the number of virtual processor cores
>      single = multiprocessing.dummy.Pool(1)
> -    multi = multiprocessing.dummy.Pool()
> +    multi = multiprocessing.dummy.Pool(jobs)
>  
>      try:
>          for p in profiles:
>              run_profile(*p)
>  
>          for pool in [single, multi]:
>              pool.close()
>              pool.join()
>      finally:
>          log.get().summary()
> diff --git a/framework/programs/run.py b/framework/programs/run.py
> index 14fb764a2..ab1cb4e24 100644
> --- a/framework/programs/run.py
> +++ b/framework/programs/run.py
> @@ -201,20 +201,28 @@ def _run_parser(input_):
>                          dest='process_isolation',
>                          action='store',
>                          type=booltype,
>                          default=core.PIGLIT_CONFIG.safe_get(
>                              'core', 'process isolation', 'true'),
>                          metavar='<bool>',
>                          help='Set this to allow tests to run without process '
>                               'isolation. This allows, but does not require, '
>                               'tests to run multiple tests per process. '
>                               'This value can also be set in piglit.conf.')
> +    parser.add_argument('-j', '--jobs',
> +                        dest='jobs',
> +                        action='store',
> +                        type=int,
> +                        default=core.PIGLIT_CONFIG.safe_get(
> +                            'core', 'jobs', None),
> +                        help='Set the maximum number of jobs to run concurrently. '
> +                             'By default, the reported number of CPUs is used.')
>      parser.add_argument("--ignore-missing",
>                          dest="ignore_missing",
>                          action="store_true",
>                          help="missing tests are considered as 'notrun'")
>      parser.add_argument("test_profile",
>                          metavar="<Profile path(s)>",
>                          nargs='+',
>                          help="Path to one or more test profiles to run. "
>                               "If more than one profile is provided then they "
>                               "will be merged.")
> @@ -289,20 +297,21 @@ def run(input_):
>      # isn't reliable with threaded run
>      if args.dmesg or args.monitored:
>          args.concurrency = "none"
>  
>      # Pass arguments into Options
>      options.OPTIONS.execute = args.execute
>      options.OPTIONS.valgrind = args.valgrind
>      options.OPTIONS.sync = args.sync
>      options.OPTIONS.deqp_mustpass = args.deqp_mustpass
>      options.OPTIONS.process_isolation = args.process_isolation
> +    options.OPTIONS.jobs = args.jobs
>  
>      # Set the platform to pass to waffle
>      options.OPTIONS.env['PIGLIT_PLATFORM'] = args.platform
>  
>      # Change working directory to the root of the piglit directory
>      piglit_dir = path.dirname(path.realpath(sys.argv[0]))
>      os.chdir(piglit_dir)
>  
>      # If the results directory already exists and if overwrite was set, then
>      # clear the directory. If it wasn't set, then raise fatal error.
> @@ -357,21 +366,21 @@ def run(input_):
>          for p in profiles:
>              p.options['ignore_missing'] = args.ignore_missing
>  
>      for p in profiles:
>          if args.exclude_tests:
>              p.filters.append(profile.RegexFilter(args.exclude_tests,
>                                                   inverse=True))
>          if args.include_tests:
>              p.filters.append(profile.RegexFilter(args.include_tests))
>  
> -    profile.run(profiles, args.log_level, backend, args.concurrency)
> +    profile.run(profiles, args.log_level, backend, args.concurrency, args.jobs)
>  
>      time_elapsed.end = time.time()
>      backend.finalize({'time_elapsed': time_elapsed.to_json()})
>  
>      print('Thank you for running Piglit!\n'
>            'Results have been written to ' + args.results_path)
>  
>  
>  @exceptions.handler
>  def resume(input_):
> @@ -382,29 +391,38 @@ def resume(input_):
>                          help="Path to results folder")
>      parser.add_argument("-f", "--config",
>                          dest="config_file",
>                          type=argparse.FileType("r"),
>                          help="Optionally specify a piglit config file to use. "
>                               "Default is piglit.conf")
>      parser.add_argument("-n", "--no-retry",
>                          dest="no_retry",
>                          action="store_true",
>                          help="Do not retry incomplete tests")
> +    parser.add_argument('-j', '--jobs',
> +                        dest='jobs',
> +                        action='store',
> +                        type=int,
> +                        default=core.PIGLIT_CONFIG.safe_get(
> +                            'core', 'jobs', None),
> +                        help='Set the maximum number of jobs to run concurrently. '
> +                             'By default, the reported number of CPUs is used.')
>      args = parser.parse_args(input_)
>      _disable_windows_exception_messages()
>  
>      results = backends.load(args.results_path)
>      options.OPTIONS.execute = results.options['execute']
>      options.OPTIONS.valgrind = results.options['valgrind']
>      options.OPTIONS.sync = results.options['sync']
>      options.OPTIONS.deqp_mustpass = results.options['deqp_mustpass']
>      options.OPTIONS.process_isolation = results.options['process_isolation']
> +    options.OPTIONS.jobs = args.jobs
>  
>      core.get_config(args.config_file)
>  
>      options.OPTIONS.env['PIGLIT_PLATFORM'] = results.options['platform']
>  
>      results.options['env'] = core.collect_system_info()
>      results.options['name'] = results.name
>  
>      # Resume only works with the JSON backend
>      backend = backends.get_backend('json')(
> @@ -446,19 +464,20 @@ def resume(input_):
>  
>          if results.options['forced_test_list']:
>              p.forced_test_list = results.options['forced_test_list']
>  
>      # This is resumed, don't bother with time since it won't be accurate anyway
>      try:
>          profile.run(
>              profiles,
>              results.options['log_level'],
>              backend,
> -            results.options['concurrent'])
> +            results.options['concurrent'],
> +            args.jobs)
>      except exceptions.PiglitUserError as e:
>          if str(e) != 'no matching tests':
>              raise
>  
>      backend.finalize()
>  
>      print("Thank you for running Piglit!\n"
>            "Results have been written to {0}".format(args.results_path))
> -- 
> 2.17.0
> 
> _______________________________________________
> Piglit mailing list
> Piglit at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/piglit
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: signature
URL: <https://lists.freedesktop.org/archives/piglit/attachments/20180530/4f1d5ec2/attachment-0001.sig>


More information about the Piglit mailing list