From 73c7abea0bd7a8700613f44aa5305374914985ba Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Mon, 31 Oct 2016 13:21:57 +0100 Subject: [PATCH 1/9] Replace wildcard dependencies by kernel query language Any given dependencies will be * treated as an instruction ID to find a match * afterwards treated as a kernel query language match expression The previously implemented wildcard matching is completely superseded by this mechanism (IMO without any semantics changes). Still TODO: Adjust textual language --- loopy/kernel/creation.py | 47 ++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index ff3bf16bc..5cde2fb4c 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -1412,43 +1412,38 @@ def apply_default_order_to_args(kernel, default_order): # }}} -# {{{ resolve wildcard insn dependencies - -def find_matching_insn_ids(knl, dep): - from fnmatch import fnmatchcase - - return [ - other_insn.id - for other_insn in knl.instructions - if fnmatchcase(other_insn.id, dep)] +# {{{ resolve instruction dependencies +def _resolve_dependencies(knl, insn, deps): + from loopy import find_instructions + from loopy.match import Id -def resove_wildcard_insn_ids(knl, deps): new_deps = [] + for dep in deps: - matches = find_matching_insn_ids(knl, dep) + # Try to match the given dependency as an instruction ID + new_dep = find_instructions(knl, Id(dep)) + # if not successful, treat the dependency as a match pattern + if not new_dep: + new_dep = find_instructions(knl, dep) - if matches: - new_deps.extend(matches) - else: - # Uh, best we can do - new_deps.append(dep) + for ndep in new_dep: + # Avoid having instructions depend on themselves + if ndep.id != insn.id: + new_deps.append(ndep.id) return frozenset(new_deps) -def resolve_wildcard_deps(knl): +def resolve_dependencies(knl): new_insns = [] for insn in knl.instructions: - if insn.depends_on is not None: - insn = insn.copy( - depends_on=resove_wildcard_insn_ids(knl, insn.depends_on), - no_sync_with=resove_wildcard_insn_ids( - knl, insn.no_sync_with), - ) - - new_insns.append(insn) + new_insns.append(insn.copy( + depends_on=_resolve_dependencies(knl, insn, insn.depends_on), + no_sync_with=_resolve_dependencies( + knl, insn, insn.no_sync_with), + )) return knl.copy(instructions=new_insns) @@ -1785,7 +1780,7 @@ def make_kernel(domains, instructions, kernel_data=["..."], **kwargs): knl = expand_defines_in_shapes(knl, defines) knl = guess_arg_shape_if_requested(knl, default_order) knl = apply_default_order_to_args(knl, default_order) - knl = resolve_wildcard_deps(knl) + knl = resolve_dependencies(knl) knl = apply_single_writer_depencency_heuristic(knl, warn_if_used=False) # ------------------------------------------------------------------------- -- GitLab From 413fc2bb28dbb8c8ad0b69c6c4cb65c0f94f5b25 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Wed, 2 Nov 2016 11:26:44 +0100 Subject: [PATCH 2/9] Fix backwards compatibility of dependency specification in textual language * dep strings from textual language are not immediately split at the colon * the given string is tried to be matched with kernel query language * If not successful, it is treated as colon separated ID list (previous semantics) The only left-over problem is instruction IDs that match query tags. We could either black list them in instruction creation or live with it, your choice. --- loopy/kernel/creation.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index 5cde2fb4c..865faf65c 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -217,22 +217,14 @@ def parse_insn_options(opt_dict, options_str, assignee_names=None): result.setdefault("inames_to_dup", []).append((value, None)) elif opt_key == "dep" and opt_value is not None: - if opt_value.startswith("*"): - result["depends_on_is_final"] = True - opt_value = (opt_value[1:]).strip() - - result["depends_on"] = frozenset( - intern(dep.strip()) for dep in opt_value.split(":") - if dep.strip()) + result['depends_on'] = frozenset([opt_value]) elif opt_key == "nosync" and opt_value is not None: if is_with_block: raise LoopyError("'nosync' option may not be specified " "in a 'with' block") - result["no_sync_with"] = frozenset( - intern(dep.strip()) for dep in opt_value.split(":") - if dep.strip()) + result['no_sync_with'] = frozenset([opt_value]) elif opt_key == "groups" and opt_value is not None: result["groups"] = frozenset( @@ -1416,16 +1408,21 @@ def apply_default_order_to_args(kernel, default_order): def _resolve_dependencies(knl, insn, deps): from loopy import find_instructions - from loopy.match import Id + from loopy.match import Or, Id new_deps = [] for dep in deps: - # Try to match the given dependency as an instruction ID - new_dep = find_instructions(knl, Id(dep)) - # if not successful, treat the dependency as a match pattern - if not new_dep: + try: + # Try to treat the given dependency a kernel query language new_dep = find_instructions(knl, dep) + except LoopyError: + # If not successful, try to match with a colon-separated list + # of instruction ID (this was the previous behaviour. + new_dep = find_instructions(knl, + Or(tuple(Id(d.strip()) + for d in dep.split(':')) + )) for ndep in new_dep: # Avoid having instructions depend on themselves -- GitLab From 985ccd5fa2e948d77c13bfab31576161f0085f14 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Wed, 2 Nov 2016 17:53:54 +0100 Subject: [PATCH 3/9] [skip ci] Fix grammar --- loopy/kernel/creation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index 865faf65c..d9cd76ccd 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -1414,7 +1414,7 @@ def _resolve_dependencies(knl, insn, deps): for dep in deps: try: - # Try to treat the given dependency a kernel query language + # Try to treat the given dependency as kernel query language new_dep = find_instructions(knl, dep) except LoopyError: # If not successful, try to match with a colon-separated list -- GitLab From cce17585b3714ef52d1d45e8aa40121b74853b61 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Thu, 10 Nov 2016 10:38:32 +0100 Subject: [PATCH 4/9] Revert "Fix backwards compatibility of dependency specification in textual language" This reverts commit 413fc2bb28dbb8c8ad0b69c6c4cb65c0f94f5b25. Conflicts: loopy/kernel/creation.py --- loopy/kernel/creation.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index d9cd76ccd..5cde2fb4c 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -217,14 +217,22 @@ def parse_insn_options(opt_dict, options_str, assignee_names=None): result.setdefault("inames_to_dup", []).append((value, None)) elif opt_key == "dep" and opt_value is not None: - result['depends_on'] = frozenset([opt_value]) + if opt_value.startswith("*"): + result["depends_on_is_final"] = True + opt_value = (opt_value[1:]).strip() + + result["depends_on"] = frozenset( + intern(dep.strip()) for dep in opt_value.split(":") + if dep.strip()) elif opt_key == "nosync" and opt_value is not None: if is_with_block: raise LoopyError("'nosync' option may not be specified " "in a 'with' block") - result['no_sync_with'] = frozenset([opt_value]) + result["no_sync_with"] = frozenset( + intern(dep.strip()) for dep in opt_value.split(":") + if dep.strip()) elif opt_key == "groups" and opt_value is not None: result["groups"] = frozenset( @@ -1408,21 +1416,16 @@ def apply_default_order_to_args(kernel, default_order): def _resolve_dependencies(knl, insn, deps): from loopy import find_instructions - from loopy.match import Or, Id + from loopy.match import Id new_deps = [] for dep in deps: - try: - # Try to treat the given dependency as kernel query language + # Try to match the given dependency as an instruction ID + new_dep = find_instructions(knl, Id(dep)) + # if not successful, treat the dependency as a match pattern + if not new_dep: new_dep = find_instructions(knl, dep) - except LoopyError: - # If not successful, try to match with a colon-separated list - # of instruction ID (this was the previous behaviour. - new_dep = find_instructions(knl, - Or(tuple(Id(d.strip()) - for d in dep.split(':')) - )) for ndep in new_dep: # Avoid having instructions depend on themselves -- GitLab From 0b7f14edf6425c2cbc0a097baba199611c75b9bf Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Thu, 10 Nov 2016 11:21:15 +0100 Subject: [PATCH 5/9] Implement dep_query= in textual assignment and change dependency resolution semantics dep_query= allows the specification of instruction dependencies with an arbitrary kernel query string. After parsing, a MatchExpression object is created and is kept in the depends_on frozenset of the instruction. When the kernel is created, those match expressions are resolved and the IDs of all matching instructions are instead added to the frozenset. When not using the textual assignment language, you can hand-construct the MatchExpression object and put it into the depends_on frozenset. --- loopy/kernel/creation.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index 5cde2fb4c..f7f835d10 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -149,7 +149,7 @@ def expand_defines_in_expr(expr, defines): def get_default_insn_options_dict(): return { - "depends_on": None, + "depends_on": frozenset(), "depends_on_is_final": False, "no_sync_with": None, "groups": frozenset(), @@ -221,9 +221,14 @@ def parse_insn_options(opt_dict, options_str, assignee_names=None): result["depends_on_is_final"] = True opt_value = (opt_value[1:]).strip() - result["depends_on"] = frozenset( + result["depends_on"] = result["depends_on"].union(frozenset( intern(dep.strip()) for dep in opt_value.split(":") - if dep.strip()) + if dep.strip())) + + elif opt_key == "dep_query" and opt_value is not None: + from loopy.match import parse_match + match = parse_match(opt_value) + result["depends_on"] = result["depends_on"].union(frozenset([match])) elif opt_key == "nosync" and opt_value is not None: if is_with_block: @@ -555,10 +560,16 @@ def parse_instructions(instructions, defines): continue elif isinstance(insn, InstructionBase): + def intern_if_str(s): + if isinstance(s, str): + return intern(s) + else: + return s + new_instructions.append( insn.copy( id=intern(insn.id) if isinstance(insn.id, str) else insn.id, - depends_on=frozenset(intern(dep) for dep in insn.depends_on), + depends_on=frozenset(intern_if_str(dep) for dep in insn.depends_on), groups=frozenset(intern(grp) for grp in insn.groups), conflicts_with_groups=frozenset( intern(grp) for grp in insn.conflicts_with_groups), @@ -1416,21 +1427,17 @@ def apply_default_order_to_args(kernel, default_order): def _resolve_dependencies(knl, insn, deps): from loopy import find_instructions - from loopy.match import Id + from loopy.match import MatchExpressionBase new_deps = [] for dep in deps: - # Try to match the given dependency as an instruction ID - new_dep = find_instructions(knl, Id(dep)) - # if not successful, treat the dependency as a match pattern - if not new_dep: - new_dep = find_instructions(knl, dep) - - for ndep in new_dep: - # Avoid having instructions depend on themselves - if ndep.id != insn.id: - new_deps.append(ndep.id) + if isinstance(dep, MatchExpressionBase): + for new_dep in find_instructions(knl, dep): + if new_dep.id != insn.id: + new_deps.append(new_dep.id) + else: + new_deps.append(dep) return frozenset(new_deps) -- GitLab From 2b5f60faf1cf37e7012a91305de81befab135b60 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Thu, 10 Nov 2016 11:41:55 +0100 Subject: [PATCH 6/9] Reenable wildcard matching in textual language --- loopy/kernel/creation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index f7f835d10..d70f884f5 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -1437,7 +1437,10 @@ def _resolve_dependencies(knl, insn, deps): if new_dep.id != insn.id: new_deps.append(new_dep.id) else: - new_deps.append(dep) + from fnmatch import fnmatchcase + for other_insn in knl.instructions: + if fnmatchcase(other_insn.id, dep): + new_deps.append(other_insn.id) return frozenset(new_deps) -- GitLab From 14b9fc6183dd2d1be3a8ac0a777a84856a6ff4e9 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Thu, 10 Nov 2016 11:50:20 +0100 Subject: [PATCH 7/9] Also introduce nosync_query and treat no_sync_with exactly like depends_on --- loopy/kernel/creation.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/loopy/kernel/creation.py b/loopy/kernel/creation.py index d70f884f5..e7edfa891 100644 --- a/loopy/kernel/creation.py +++ b/loopy/kernel/creation.py @@ -151,7 +151,7 @@ def get_default_insn_options_dict(): return { "depends_on": frozenset(), "depends_on_is_final": False, - "no_sync_with": None, + "no_sync_with": frozenset(), "groups": frozenset(), "conflicts_with_groups": frozenset(), "insn_id": None, @@ -235,9 +235,19 @@ def parse_insn_options(opt_dict, options_str, assignee_names=None): raise LoopyError("'nosync' option may not be specified " "in a 'with' block") - result["no_sync_with"] = frozenset( + result["no_sync_with"] = result["no_sync_with"].union(frozenset( intern(dep.strip()) for dep in opt_value.split(":") - if dep.strip()) + if dep.strip())) + + elif opt_key == "nosync_query" and opt_value is not None: + if is_with_block: + raise LoopyError("'nosync' option may not be specified " + "in a 'with' block") + + from loopy.match import parse_match + match = parse_match(opt_value) + result["no_sync_with"] = result["no_sync_with"].union( + frozenset([match])) elif opt_key == "groups" and opt_value is not None: result["groups"] = frozenset( -- GitLab From e426a43c244585c101734e72d87cb119760f03e3 Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Fri, 11 Nov 2016 10:48:14 +0100 Subject: [PATCH 8/9] [skip ci] Fix documentation of the Instruction class --- loopy/kernel/instruction.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index c54d1fc32..528916245 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -51,6 +51,17 @@ class InstructionBase(Record): May be *None* to invoke the default. + There are two extensions to this: + + - You may use `*` as a wildcard in the given IDs. This will be expanded + to all matching instruction IDs during :func:`loopy.make_kernel`. + - Instead of an instruction ID, you may pass an instance of + :class:`loopy.match.MatchExpressionBase` into the :attr:`depends_on` + :class:`frozenset`. The given expression will be used to add any + matching instructions in the kernel to :attr:`depends_on` during + :func:`loopy.make_kernel`. Note, that this is not meant as a user-facing + interface. + .. attribute:: depends_on_is_final A :class:`bool` determining whether :attr:`depends_on` constitutes @@ -82,7 +93,10 @@ class InstructionBase(Record): a :class:`frozenset` of :attr:`id` values of :class:`Instruction` instances with which no barrier synchronization is necessary, even given the existence - of a dependency chain and apparently conflicting access + of a dependency chain and apparently conflicting access. + + Note, that :attr:`no_sync_with` allows instruction matching through wildcards + and match expression, just like :attr:`depends_on`. .. rubric:: Conditionals -- GitLab From a4d4dc434330fac93e30f68bc5ab3405d0c796da Mon Sep 17 00:00:00 2001 From: Dominic Kempf Date: Fri, 11 Nov 2016 12:57:22 +0100 Subject: [PATCH 9/9] Add *_query to the documentation of the textual language --- doc/ref_kernel.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/ref_kernel.rst b/doc/ref_kernel.rst index 3a15e3a58..97d71f3e0 100644 --- a/doc/ref_kernel.rst +++ b/doc/ref_kernel.rst @@ -242,6 +242,12 @@ These are usually key-value pairs. The following attributes are recognized: heuristic and indicate that the specified list of dependencies is exhaustive. +* ``dep_query=...`` provides an alternative way of specifying instruction + dependencies. The given string is parsed as a match expression object by + :func:`loopy.match.parse_match`. Upon kernel generation, this match + expression is used to match instructions in the kernel and add them as + dependencies. + * ``nosync=id1:id2`` prescribes that no barrier synchronization is necessary the instructions with identifiers ``id1`` and ``id2`` to the, even if a dependency chain exists and variables are accessed in an apparently @@ -251,6 +257,9 @@ These are usually key-value pairs. The following attributes are recognized: function :func:`fnmatch.fnmatchcase`. This is helpful in conjunction with ``id_prefix``. +* ``nosync_query=...`` provides an alternative way of specifying ``nosync``, + just like ``dep_query`` and ``dep``. + * ``priority=integer`` sets the instructions priority to the value ``integer``. Instructions with higher priority will be scheduled sooner, if possible. Note that the scheduler may still schedule a lower-priority -- GitLab