From cd59be5b64ceeac2c658ac6d56e9e4f88aaab389 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 03:53:15 -0500 Subject: [PATCH 01/62] add dependencies attribute to instruction (copying in changes from downstream PR to create intermediate PR) --- loopy/kernel/instruction.py | 67 +++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 81b174653..ab4082c70 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -212,6 +212,7 @@ class InstructionBase(ImmutableRecord, Taggable): pymbolic_set_fields = {"predicates"} def __init__(self, id, depends_on, depends_on_is_final, + dependencies, groups, conflicts_with_groups, no_sync_with, within_inames_is_final, within_inames, @@ -241,6 +242,9 @@ def __init__(self, id, depends_on, depends_on_is_final, if depends_on is None: depends_on = frozenset() + if dependencies is None: + dependencies = {} + if groups is None: groups = frozenset() @@ -297,6 +301,7 @@ def __init__(self, id, depends_on, depends_on_is_final, id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, no_sync_with=no_sync_with, groups=groups, conflicts_with_groups=conflicts_with_groups, within_inames_is_final=within_inames_is_final, @@ -388,6 +393,7 @@ def get_str_options(self): if self.depends_on: result.append("dep="+":".join(self.depends_on)) + # TODO something with dependencies? if self.no_sync_with: result.append("nosync="+":".join( "%s@%s" % entry for entry in self.no_sync_with)) @@ -457,6 +463,7 @@ def __setstate__(self, val): if self.id is not None: # pylint:disable=access-member-before-definition self.id = intern(self.id) self.depends_on = intern_frozenset_of_ids(self.depends_on) + # TODO something with dependencies? self.groups = intern_frozenset_of_ids(self.groups) self.conflicts_with_groups = ( intern_frozenset_of_ids(self.conflicts_with_groups)) @@ -874,6 +881,7 @@ def __init__(self, id=None, depends_on=None, depends_on_is_final=None, + dependencies=None, groups=None, conflicts_with_groups=None, no_sync_with=None, @@ -887,6 +895,7 @@ def __init__(self, id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, groups=groups, conflicts_with_groups=conflicts_with_groups, no_sync_with=no_sync_with, @@ -1005,6 +1014,7 @@ def __init__(self, id=None, depends_on=None, depends_on_is_final=None, + dependencies=None, groups=None, conflicts_with_groups=None, no_sync_with=None, @@ -1018,6 +1028,7 @@ def __init__(self, id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, groups=groups, conflicts_with_groups=conflicts_with_groups, no_sync_with=no_sync_with, @@ -1179,13 +1190,20 @@ class CInstruction(InstructionBase): def __init__(self, iname_exprs, code, - read_variables=frozenset(), assignees=tuple(), - id=None, depends_on=None, depends_on_is_final=None, - groups=None, conflicts_with_groups=None, + read_variables=frozenset(), + assignees=tuple(), + id=None, + depends_on=None, + depends_on_is_final=None, + dependencies=None, + groups=None, + conflicts_with_groups=None, no_sync_with=None, - within_inames_is_final=None, within_inames=None, + within_inames_is_final=None, + within_inames=None, priority=0, - predicates=frozenset(), tags=None): + predicates=frozenset(), + tags=None): """ :arg iname_exprs: Like :attr:`iname_exprs`, but instead of tuples, simple strings pepresenting inames are also allowed. A single @@ -1200,11 +1218,13 @@ def __init__(self, id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, groups=groups, conflicts_with_groups=conflicts_with_groups, no_sync_with=no_sync_with, within_inames_is_final=within_inames_is_final, within_inames=within_inames, - priority=priority, predicates=predicates, tags=tags) + priority=priority, predicates=predicates, + tags=tags) # {{{ normalize iname_exprs @@ -1339,16 +1359,25 @@ class NoOpInstruction(_DataObliviousInstruction): ... nop """ - def __init__(self, id=None, depends_on=None, depends_on_is_final=None, - groups=None, conflicts_with_groups=None, + def __init__( + self, + id=None, + depends_on=None, + depends_on_is_final=None, + dependencies=None, + groups=None, + conflicts_with_groups=None, no_sync_with=None, - within_inames_is_final=None, within_inames=None, + within_inames_is_final=None, + within_inames=None, priority=None, - predicates=None, tags=None): + predicates=None, + tags=None): super().__init__( id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, groups=groups, conflicts_with_groups=conflicts_with_groups, no_sync_with=no_sync_with, @@ -1398,12 +1427,21 @@ class BarrierInstruction(_DataObliviousInstruction): fields = _DataObliviousInstruction.fields | {"synchronization_kind", "mem_kind"} - def __init__(self, id, depends_on=None, depends_on_is_final=None, - groups=None, conflicts_with_groups=None, + def __init__( + self, + id, + depends_on=None, + depends_on_is_final=None, + dependencies=None, + groups=None, + conflicts_with_groups=None, no_sync_with=None, - within_inames_is_final=None, within_inames=None, + within_inames_is_final=None, + within_inames=None, priority=None, - predicates=None, tags=None, synchronization_kind="global", + predicates=None, + tags=None, + synchronization_kind="global", mem_kind="local"): if predicates: @@ -1413,6 +1451,7 @@ def __init__(self, id, depends_on=None, depends_on_is_final=None, id=id, depends_on=depends_on, depends_on_is_final=depends_on_is_final, + dependencies=dependencies, groups=groups, conflicts_with_groups=conflicts_with_groups, no_sync_with=no_sync_with, From c03636d3c74d21a56143f98c8fbacab0c887ccb6 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 03:54:32 -0500 Subject: [PATCH 02/62] create function to add new dependencies, add_stmt_inst_dependency() (copying in changes from downstream PR to create intermediate PR) --- loopy/transform/instruction.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 055384ff1..b1e9cb7a5 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -117,6 +117,42 @@ def add_dep(insn): # }}} +# {{{ add_stmt_inst_dependency + +def add_stmt_inst_dependency( + kernel, stmt_id, depends_on_id, new_dependency): + """Add the statement instance dependency *new_dependency* to statement with + id *stmt_id*. + """ + + if stmt_id not in kernel.id_to_insn: + raise LoopyError("no instructions found matching '%s'," + "cannot add dependency %s->%s" + % (stmt_id, depends_on_id, stmt_id)) + if depends_on_id not in kernel.id_to_insn: + raise LoopyError("no instructions found matching '%s'," + "cannot add dependency %s->%s" + % (depends_on_id, depends_on_id, stmt_id)) + + matched = [False] + + def _add_dep(stmt): + new_deps_dict = stmt.dependencies # dict mapping depends-on ids to dep maps + matched[0] = True + new_deps_dict.setdefault(depends_on_id, []).append(new_dependency) + return stmt.copy(dependencies=new_deps_dict) + + result = map_instructions(kernel, "id:%s" % (stmt_id), _add_dep) + + if not matched[0]: # Is this possible, given check above? + raise LoopyError("no instructions found matching '%s' " + "(to which dependencies would be added)" % stmt_id) + + return result + +# }}} + + # {{{ remove_instructions def remove_instructions(kernel, insn_ids): From 8a2e7ff09ff98572a7906e7168b265321a4659b4 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 04:02:10 -0500 Subject: [PATCH 03/62] create function to ensure linearization satisfies deps, check_linearization_validity() (copying in changes from downstream PR to create intermediate PR) --- loopy/schedule/checker/__init__.py | 100 +++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 6a2ecb9c5..e3d4d0090 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -136,3 +136,103 @@ def get_pairwise_statement_orderings( # }}} # }}} + + +def check_linearization_validity( + knl, + linearization_items, + ): + # TODO document + + from loopy.schedule.checker.utils import ( + prettier_map_string, + ) + + # {{{ make sure kernel has been preprocessed + + # note: kernels must always be preprocessed before scheduling + from loopy.kernel import KernelState + assert knl.state in [ + KernelState.PREPROCESSED, + KernelState.LINEARIZED] + + # }}} + + # {{{ Create map from dependent instruction id pairs to dependencies + + # To minimize time complexity, all pairwise schedules will be created + # in one pass, which first requires finding all pairs of statements involved + # in deps. + # So, since we have to find these pairs anyway, collect their deps at + # the same time so we don't have to do it again later during lin checking. + + stmts_to_deps = {} + for insn_after in knl.instructions: + for before_id, dep_list in insn_after.dependencies.items(): + stmts_to_deps.setdefault( + (before_id, insn_after.id), []).extend(dep_list) + # }}} + + pworders = get_pairwise_statement_orderings( + knl, + linearization_items, + stmts_to_deps.keys(), + ) + + # For each dependency, create+test linearization containing pair of insns------ + linearization_is_valid = True + for (insn_id_before, insn_id_after), dependencies in stmts_to_deps.items(): + + # Get pairwise ordering info for stmts involved in the dependency + pworder = pworders[(insn_id_before, insn_id_after)] + + # check each dep for this statement pair + for dependency in dependencies: + + # reorder variables/params in constraint map space to match SIO so we can + # check to see whether the constraint map is a subset of the SIO + # (spaces must be aligned so that the variables in the constraint map + # correspond to the same variables in the SIO) + from loopy.schedule.checker.utils import ( + ensure_dim_names_match_and_align, + ) + + aligned_dep_map = ensure_dim_names_match_and_align( + dependency, pworder.sio_intra_thread) + + assert aligned_dep_map.space == pworder.sio_intra_thread.space + assert aligned_dep_map.space == pworder.sio_intra_group.space + assert aligned_dep_map.space == pworder.sio_global.space + assert (aligned_dep_map.get_var_dict() == + pworder.sio_intra_thread.get_var_dict()) + assert (aligned_dep_map.get_var_dict() == + pworder.sio_intra_group.get_var_dict()) + assert (aligned_dep_map.get_var_dict() == + pworder.sio_global.get_var_dict()) + + if not aligned_dep_map.is_subset( + pworder.sio_intra_thread | + pworder.sio_intra_group | + pworder.sio_global + ): + + linearization_is_valid = False + + print("================ constraint check failure =================") + print("Constraint map not subset of SIO") + print("Dependencies:") + print(insn_id_before+"->"+insn_id_after) + print(prettier_map_string(dependency)) + print("Statement instance ordering:") + print(prettier_map_string(pworder.sio_intra_thread)) + print("dependency.gist(pworder.sio_intra_thread):") + print(prettier_map_string( + aligned_dep_map.gist(pworder.sio_intra_thread))) + print("pworder.sio_intra_thread.gist(dependency)") + print(prettier_map_string( + pworder.sio_intra_thread.gist(aligned_dep_map))) + print("Loop priority known:") + print(knl.loop_priority) + print("===========================================================") + + return linearization_is_valid From 10f2ff57411c71ce03f925771d5b34a9bc495508 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 04:03:45 -0500 Subject: [PATCH 04/62] make check_linearization_validity() and add_stmt_inst_dependency() global loopy funcs (copying in changes from downstream PR to create intermediate PR) --- loopy/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index 9c4bfa6d0..8cec9c6ac 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -78,7 +78,8 @@ from loopy.transform.instruction import ( find_instructions, map_instructions, - set_instruction_priority, add_dependency, + set_instruction_priority, + add_dependency, add_stmt_inst_dependency, remove_instructions, replace_instruction_ids, tag_instructions, @@ -121,6 +122,8 @@ from loopy.preprocess import preprocess_kernel, realize_reduction from loopy.schedule import ( generate_loop_schedules, get_one_scheduled_kernel, get_one_linearized_kernel) +from loopy.schedule.checker import ( + check_linearization_validity) from loopy.statistics import (ToCountMap, CountGranularity, stringify_stats_mapping, Op, MemAccess, get_op_map, get_mem_access_map, get_synchronization_map, gather_access_footprints, @@ -202,7 +205,8 @@ "rename_argument", "set_temporary_scope", "find_instructions", "map_instructions", - "set_instruction_priority", "add_dependency", + "set_instruction_priority", + "add_dependency", "add_stmt_inst_dependency", "remove_instructions", "replace_instruction_ids", "tag_instructions", @@ -247,6 +251,7 @@ "preprocess_kernel", "realize_reduction", "generate_loop_schedules", "get_one_scheduled_kernel", "get_one_linearized_kernel", + "check_linearization_validity", "GeneratedProgram", "CodeGenerationResult", "PreambleInfo", "generate_code", "generate_code_v2", "generate_body", From a4deb28837a3bae78242938a7547687adec6fee2 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 04:05:03 -0500 Subject: [PATCH 05/62] create test for adding dependencies to statements (copying in changes from downstream PR to create intermediate PR) --- test/test_linearization_checker.py | 137 ++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 10 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index fa197bccd..00e35ec5f 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -134,7 +134,7 @@ def test_pairwise_schedule_creation(): # Get a linearization proc_knl = preprocess_kernel(knl) lin_knl = get_one_linearized_kernel(proc_knl) - linearization_items = lin_knl.linearization + lin_items = lin_knl.linearization insn_id_pairs = [ ("stmt_a", "stmt_b"), @@ -146,7 +146,7 @@ def test_pairwise_schedule_creation(): ] pworders = get_pairwise_statement_orderings( lin_knl, - linearization_items, + lin_items, insn_id_pairs, ) @@ -345,14 +345,14 @@ def test_pairwise_schedule_creation_with_hw_par_tags(): # Get a linearization proc_knl = preprocess_kernel(knl) lin_knl = get_one_linearized_kernel(proc_knl) - linearization_items = lin_knl.linearization + lin_items = lin_knl.linearization stmt_id_pairs = [ ("stmt_a", "stmt_b"), ] pworders = get_pairwise_statement_orderings( lin_knl, - linearization_items, + lin_items, stmt_id_pairs, ) @@ -522,7 +522,7 @@ def test_statement_instance_ordering(): # Get a linearization knl = preprocess_kernel(knl) knl = get_one_linearized_kernel(knl) - linearization_items = knl.linearization + lin_items = knl.linearization # Get pairwise schedules stmt_id_pairs = [ @@ -535,7 +535,7 @@ def test_statement_instance_ordering(): ] pworders = get_pairwise_statement_orderings( knl, - linearization_items, + lin_items, stmt_id_pairs, ) @@ -650,7 +650,7 @@ def test_statement_instance_ordering_with_hw_par_tags(): # Get a linearization proc_knl = preprocess_kernel(knl) lin_knl = get_one_linearized_kernel(proc_knl) - linearization_items = lin_knl.linearization + lin_items = lin_knl.linearization # Get pairwise schedules stmt_id_pairs = [ @@ -658,7 +658,7 @@ def test_statement_instance_ordering_with_hw_par_tags(): ] pworders = get_pairwise_statement_orderings( lin_knl, - linearization_items, + lin_items, stmt_id_pairs, ) @@ -734,11 +734,11 @@ def test_sios_and_schedules_with_barriers(): # Get a linearization proc_knl = preprocess_kernel(knl) lin_knl = get_one_linearized_kernel(proc_knl) - linearization_items = lin_knl.linearization + lin_items = lin_knl.linearization insn_id_pairs = [("j1", "2"), ("1", "i0")] pworders = get_pairwise_statement_orderings( - lin_knl, linearization_items, insn_id_pairs) + lin_knl, lin_items, insn_id_pairs) # Relationship between j1 and 2 -------------------------------------------- @@ -1004,6 +1004,123 @@ def test_sios_and_schedules_with_barriers(): # }}} +def test_add_stmt_inst_dependencies(): + + lp.set_caching_enabled(False) + # TODO REMOVE THIS^ (prevents + # TypeError: unsupported type for persistent hash keying: + # ) during preprocessing + + # Make kernel and use OLD deps to linearize correctly for now + i_range_str = "0 <= i < pi" + i_range_str_p = "0 <= i' < pi" + assumptions_str = "pi >= 1" + knl = lp.make_kernel( + "{[i]: %s}" % (i_range_str), + """ + a[i] = 3.14 {id=stmt_a} + b[i] = a[i] {id=stmt_b, dep=stmt_a} + c[i] = b[i] {id=stmt_c, dep=stmt_b} + """, + name="example", + assumptions=assumptions_str, + lang_version=(2018, 2) + ) + knl = lp.add_and_infer_dtypes( + knl, {"a": np.float32, "b": np.float32, "c": np.float32}) + + for stmt in knl.instructions: + assert not stmt.dependencies + + # Add a dependency to stmt_b + dep_b_on_a = _isl_map_with_marked_dims( + "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i > i' " + "and {1} and {2} and {3} }}".format( + STATEMENT_VAR_NAME, + i_range_str, + i_range_str_p, + assumptions_str, + )) + + knl = lp.add_stmt_inst_dependency(knl, "stmt_b", "stmt_a", dep_b_on_a) + + for stmt in knl.instructions: + if stmt.id == "stmt_b": + assert stmt.dependencies == { + "stmt_a": [dep_b_on_a, ], + } + else: + assert not stmt.dependencies + + # Add a second dependency to stmt_b + dep_b_on_a_2 = _isl_map_with_marked_dims( + "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i = i' " + "and {1} and {2} and {3} }}".format( + STATEMENT_VAR_NAME, + i_range_str, + i_range_str_p, + assumptions_str, + )) + + knl = lp.add_stmt_inst_dependency(knl, "stmt_b", "stmt_a", dep_b_on_a_2) + + for stmt in knl.instructions: + if stmt.id == "stmt_b": + assert stmt.dependencies == { + "stmt_a": [dep_b_on_a, dep_b_on_a_2], + } + else: + assert not stmt.dependencies + + # Add dependencies to stmt_c + + dep_c_on_a = _isl_map_with_marked_dims( + "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i >= i' " + "and {1} and {2} and {3} }}".format( + STATEMENT_VAR_NAME, + i_range_str, + i_range_str_p, + assumptions_str, + )) + dep_c_on_b = _isl_map_with_marked_dims( + "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i >= i' " + "and {1} and {2} and {3} }}".format( + STATEMENT_VAR_NAME, + i_range_str, + i_range_str_p, + assumptions_str, + )) + + knl = lp.add_stmt_inst_dependency(knl, "stmt_c", "stmt_a", dep_c_on_a) + knl = lp.add_stmt_inst_dependency(knl, "stmt_c", "stmt_b", dep_c_on_b) + + for stmt in knl.instructions: + if stmt.id == "stmt_b": + assert stmt.dependencies == { + "stmt_a": [dep_b_on_a, dep_b_on_a_2], + } + elif stmt.id == "stmt_c": + assert stmt.dependencies == { + "stmt_a": [dep_c_on_a, ], + "stmt_b": [dep_c_on_b, ], + } + else: + assert not stmt.dependencies + + # Now make sure deps are satisfied + proc_knl = preprocess_kernel(knl) + lin_knl = get_one_linearized_kernel(proc_knl) + lin_items = lin_knl.linearization + + linearization_is_valid = lp.check_linearization_validity( + proc_knl, lin_items) + + assert linearization_is_valid + + +# TODO create more kernels with valid/invalid linearizations to test checker + + if __name__ == "__main__": if len(sys.argv) > 1: exec(sys.argv[1]) From b62e109879d0299f157869ab359d5e267569807c Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 2 Apr 2021 04:20:57 -0500 Subject: [PATCH 06/62] rename check_linearization_validity()->check_dependency_satisfaction() --- loopy/__init__.py | 4 ++-- loopy/schedule/checker/__init__.py | 2 +- test/test_linearization_checker.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index 8cec9c6ac..e7a66ed29 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -123,7 +123,7 @@ from loopy.schedule import ( generate_loop_schedules, get_one_scheduled_kernel, get_one_linearized_kernel) from loopy.schedule.checker import ( - check_linearization_validity) + check_dependency_satisfaction) from loopy.statistics import (ToCountMap, CountGranularity, stringify_stats_mapping, Op, MemAccess, get_op_map, get_mem_access_map, get_synchronization_map, gather_access_footprints, @@ -251,7 +251,7 @@ "preprocess_kernel", "realize_reduction", "generate_loop_schedules", "get_one_scheduled_kernel", "get_one_linearized_kernel", - "check_linearization_validity", + "check_dependency_satisfaction", "GeneratedProgram", "CodeGenerationResult", "PreambleInfo", "generate_code", "generate_code_v2", "generate_body", diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index e3d4d0090..0fcee4831 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -138,7 +138,7 @@ def get_pairwise_statement_orderings( # }}} -def check_linearization_validity( +def check_dependency_satisfaction( knl, linearization_items, ): diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 00e35ec5f..7a3838460 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1112,7 +1112,7 @@ def test_add_stmt_inst_dependencies(): lin_knl = get_one_linearized_kernel(proc_knl) lin_items = lin_knl.linearization - linearization_is_valid = lp.check_linearization_validity( + linearization_is_valid = lp.check_dependency_satisfaction( proc_knl, lin_items) assert linearization_is_valid From a6feb6d9bd29204819be08334c78249e359013db Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sun, 4 Apr 2021 22:21:15 -0500 Subject: [PATCH 07/62] return info about unsatisfied deps from check_dependency_satisfaction(); also clean up code/comments --- loopy/schedule/checker/__init__.py | 107 ++++++++++++++++------------- test/test_linearization_checker.py | 7 +- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 0fcee4831..4ef952100 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -26,7 +26,7 @@ def get_pairwise_statement_orderings( knl, lin_items, - insn_id_pairs, + stmt_id_pairs, ): r"""For each statement pair in a subset of all statement pairs found in a linearized kernel, determine the (relative) order in which the statement @@ -44,10 +44,10 @@ def get_pairwise_statement_orderings( this routine during linearization, a truncated (i.e. partial) linearization may be passed through this argument. - :arg insn_id_pairs: A list containing pairs of instruction identifiers. + :arg stmt_id_pairs: A list containing pairs of instruction identifiers. :returns: A dictionary mapping each two-tuple of instruction identifiers - provided in `insn_id_pairs` to a :class:`collections.namedtuple` + provided in `stmt_id_pairs` to a :class:`collections.namedtuple` containing the intra-thread SIO (`sio_intra_thread`), intra-group SIO (`sio_intra_group`), and global SIO (`sio_global`), each realized as an :class:`islpy.Map` from each instance of the first @@ -68,8 +68,8 @@ def get_pairwise_statement_orderings( >>> knl = lp.make_kernel( ... "{[j,k]: 0<=j>> knl = lp.add_and_infer_dtypes(knl, {"a": np.float32, "b": np.float32}) >>> # Get a linearization @@ -79,10 +79,10 @@ def get_pairwise_statement_orderings( >>> sio_dict = get_pairwise_statement_orderings( ... knl, ... knl.linearization, - ... [("insn_a", "insn_b")], + ... [("stmt_a", "stmt_b")], ... ) >>> # Print map - >>> print(str(sio_dict[("insn_a", "insn_b")].sio_intra_thread + >>> print(str(sio_dict[("stmt_a", "stmt_b")].sio_intra_thread ... ).replace("{ ", "{\n").replace(" :", "\n:")) [pj, pk] -> { [_lp_linchk_stmt' = 0, j', k'] -> [_lp_linchk_stmt = 1, j, k] @@ -129,7 +129,7 @@ def get_pairwise_statement_orderings( return get_pairwise_statement_orderings_inner( knl, lin_items, - insn_id_pairs, + stmt_id_pairs, loops_to_ignore=conc_loop_inames, ) @@ -142,6 +142,7 @@ def check_dependency_satisfaction( knl, linearization_items, ): + # TODO document from loopy.schedule.checker.utils import ( @@ -150,7 +151,7 @@ def check_dependency_satisfaction( # {{{ make sure kernel has been preprocessed - # note: kernels must always be preprocessed before scheduling + # Note: kernels must always be preprocessed before scheduling from loopy.kernel import KernelState assert knl.state in [ KernelState.PREPROCESSED, @@ -162,44 +163,61 @@ def check_dependency_satisfaction( # To minimize time complexity, all pairwise schedules will be created # in one pass, which first requires finding all pairs of statements involved - # in deps. - # So, since we have to find these pairs anyway, collect their deps at - # the same time so we don't have to do it again later during lin checking. - - stmts_to_deps = {} - for insn_after in knl.instructions: - for before_id, dep_list in insn_after.dependencies.items(): - stmts_to_deps.setdefault( - (before_id, insn_after.id), []).extend(dep_list) + # in deps. We will also need to collect the deps for each statement pair, + # so do this at the same time. + + stmt_pairs_to_deps = {} + + # stmt_pairs_to_deps: + # {(stmt_id_before1, stmt_id_after1): [dep1, dep2, ...], + # (stmt_id_before2, stmt_id_after2): [dep1, dep2, ...], + # ...} + + for stmt_after in knl.instructions: + for before_id, dep_list in stmt_after.dependencies.items(): + # (don't compare dep maps to maps found; duplicate deps should be rare) + stmt_pairs_to_deps.setdefault( + (before_id, stmt_after.id), []).extend(dep_list) # }}} + # {{{ Get statement instance orderings + pworders = get_pairwise_statement_orderings( knl, linearization_items, - stmts_to_deps.keys(), + stmt_pairs_to_deps.keys(), ) - # For each dependency, create+test linearization containing pair of insns------ - linearization_is_valid = True - for (insn_id_before, insn_id_after), dependencies in stmts_to_deps.items(): + # }}} + + # {{{ For each depender-dependee pair of statements, check all deps vs. SIO + + deps_are_satisfied = True - # Get pairwise ordering info for stmts involved in the dependency - pworder = pworders[(insn_id_before, insn_id_after)] + # Collect info about unsatisfied deps + unsatisfied_deps = [] + from collections import namedtuple + UnsatisfiedDependencyInfo = namedtuple( + "UnsatisfiedDependencyInfo", + ["statement_pair", "dependency", "statement_ordering"]) - # check each dep for this statement pair + for stmt_id_pair, dependencies in stmt_pairs_to_deps.items(): + + # Get the pairwise ordering info (includes SIOs) + pworder = pworders[stmt_id_pair] + + # Check each dep for this statement pair for dependency in dependencies: - # reorder variables/params in constraint map space to match SIO so we can + # Align constraint map space to match SIO so we can # check to see whether the constraint map is a subset of the SIO - # (spaces must be aligned so that the variables in the constraint map - # correspond to the same variables in the SIO) from loopy.schedule.checker.utils import ( ensure_dim_names_match_and_align, ) - aligned_dep_map = ensure_dim_names_match_and_align( dependency, pworder.sio_intra_thread) + # Spaces must match assert aligned_dep_map.space == pworder.sio_intra_thread.space assert aligned_dep_map.space == pworder.sio_intra_group.space assert aligned_dep_map.space == pworder.sio_global.space @@ -210,29 +228,20 @@ def check_dependency_satisfaction( assert (aligned_dep_map.get_var_dict() == pworder.sio_global.get_var_dict()) + # Check dependency if not aligned_dep_map.is_subset( pworder.sio_intra_thread | pworder.sio_intra_group | pworder.sio_global ): - linearization_is_valid = False - - print("================ constraint check failure =================") - print("Constraint map not subset of SIO") - print("Dependencies:") - print(insn_id_before+"->"+insn_id_after) - print(prettier_map_string(dependency)) - print("Statement instance ordering:") - print(prettier_map_string(pworder.sio_intra_thread)) - print("dependency.gist(pworder.sio_intra_thread):") - print(prettier_map_string( - aligned_dep_map.gist(pworder.sio_intra_thread))) - print("pworder.sio_intra_thread.gist(dependency)") - print(prettier_map_string( - pworder.sio_intra_thread.gist(aligned_dep_map))) - print("Loop priority known:") - print(knl.loop_priority) - print("===========================================================") - - return linearization_is_valid + deps_are_satisfied = False + + unsatisfied_deps.append( + UnsatisfiedDependencyInfo(stmt_id_pair, aligned_dep_map, pworder)) + + # Could break here if we don't care about remaining deps + + # }}} + + return deps_are_satisfied, unsatisfied_deps diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 26075dc97..81000c8ae 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1266,7 +1266,7 @@ def test_sios_and_schedules_with_vec_and_barriers(): # }}} -def test_add_stmt_inst_dependencies(): +def test_add_stmt_inst_dependency(): lp.set_caching_enabled(False) # TODO REMOVE THIS^ (prevents @@ -1374,10 +1374,11 @@ def test_add_stmt_inst_dependencies(): lin_knl = get_one_linearized_kernel(proc_knl) lin_items = lin_knl.linearization - linearization_is_valid = lp.check_dependency_satisfaction( + deps_are_satisfied, unsatisfied_deps = lp.check_dependency_satisfaction( proc_knl, lin_items) - assert linearization_is_valid + assert deps_are_satisfied + assert not unsatisfied_deps # TODO create more kernels with valid/invalid linearizations to test checker From daedf107a0ed6a2f02062af9c0d2c00bbc986360 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sun, 4 Apr 2021 22:29:25 -0500 Subject: [PATCH 08/62] rename check_dependency_satisfaction()->find_unsatisfied_dependencies(); eliminate redundant bool return value for dep satisfaction and instead just check if unsatisfied dep list is empty --- loopy/__init__.py | 4 ++-- loopy/schedule/checker/__init__.py | 20 ++++++++------------ test/test_linearization_checker.py | 3 +-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index e7a66ed29..f94cbe021 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -123,7 +123,7 @@ from loopy.schedule import ( generate_loop_schedules, get_one_scheduled_kernel, get_one_linearized_kernel) from loopy.schedule.checker import ( - check_dependency_satisfaction) + find_unsatisfied_dependencies) from loopy.statistics import (ToCountMap, CountGranularity, stringify_stats_mapping, Op, MemAccess, get_op_map, get_mem_access_map, get_synchronization_map, gather_access_footprints, @@ -251,7 +251,7 @@ "preprocess_kernel", "realize_reduction", "generate_loop_schedules", "get_one_scheduled_kernel", "get_one_linearized_kernel", - "check_dependency_satisfaction", + "find_unsatisfied_dependencies", "GeneratedProgram", "CodeGenerationResult", "PreambleInfo", "generate_code", "generate_code_v2", "generate_body", diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 4ef952100..d418047bb 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -138,17 +138,15 @@ def get_pairwise_statement_orderings( # }}} -def check_dependency_satisfaction( +# {{{ find_unsatisfied_dependencies() + +def find_unsatisfied_dependencies( knl, linearization_items, ): # TODO document - from loopy.schedule.checker.utils import ( - prettier_map_string, - ) - # {{{ make sure kernel has been preprocessed # Note: kernels must always be preprocessed before scheduling @@ -192,8 +190,6 @@ def check_dependency_satisfaction( # {{{ For each depender-dependee pair of statements, check all deps vs. SIO - deps_are_satisfied = True - # Collect info about unsatisfied deps unsatisfied_deps = [] from collections import namedtuple @@ -235,13 +231,13 @@ def check_dependency_satisfaction( pworder.sio_global ): - deps_are_satisfied = False - - unsatisfied_deps.append( - UnsatisfiedDependencyInfo(stmt_id_pair, aligned_dep_map, pworder)) + unsatisfied_deps.append(UnsatisfiedDependencyInfo( + stmt_id_pair, aligned_dep_map, pworder)) # Could break here if we don't care about remaining deps # }}} - return deps_are_satisfied, unsatisfied_deps + return unsatisfied_deps + +# }}} diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 81000c8ae..700c443f5 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1374,10 +1374,9 @@ def test_add_stmt_inst_dependency(): lin_knl = get_one_linearized_kernel(proc_knl) lin_items = lin_knl.linearization - deps_are_satisfied, unsatisfied_deps = lp.check_dependency_satisfaction( + unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) - assert deps_are_satisfied assert not unsatisfied_deps From 0446d3db0bd7476382aa141c3c852fe7f999f1f3 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sun, 4 Apr 2021 22:37:00 -0500 Subject: [PATCH 09/62] renaming instruction->statement --- loopy/schedule/checker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 8e0d9db1f..82f9cb5fc 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -157,7 +157,7 @@ def find_unsatisfied_dependencies( # }}} - # {{{ Create map from dependent instruction id pairs to dependencies + # {{{ Create map from dependent statement id pairs to dependencies # To minimize time complexity, all pairwise schedules will be created # in one pass, which first requires finding all pairs of statements involved From 03fa0c97f43012d604dfa5771931ec0ff5f49481 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 5 Apr 2021 04:45:38 -0500 Subject: [PATCH 10/62] add update_for_Map to LoopyKeyBuilder --- loopy/tools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loopy/tools.py b/loopy/tools.py index 5be4ca6b5..bdba32b90 100644 --- a/loopy/tools.py +++ b/loopy/tools.py @@ -78,6 +78,8 @@ def update_for_BasicSet(self, key_hash, key): # noqa getattr(prn, "print_"+key._base_name)(key) key_hash.update(prn.get_str().encode("utf8")) + update_for_Map = update_for_BasicSet # noqa + def update_for_type(self, key_hash, key): try: method = getattr(self, "update_for_type_"+key.__name__) From 50332c37e09cbceff4da7ecdd20a28e2d7bb5bab Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 5 Apr 2021 06:05:12 -0500 Subject: [PATCH 11/62] change BEFORE_MARK to underscore since ISL ignores apostrophes; now caching works --- loopy/schedule/checker/__init__.py | 4 ++-- loopy/schedule/checker/schedule.py | 2 +- test/test_linearization_checker.py | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 82f9cb5fc..5811f3048 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -85,8 +85,8 @@ def get_pairwise_statement_orderings( >>> print(str(sio_dict[("stmt_a", "stmt_b")].sio_intra_thread ... ).replace("{ ", "{\n").replace(" :", "\n:")) [pj, pk] -> { - [_lp_linchk_stmt' = 0, j', k'] -> [_lp_linchk_stmt = 1, j, k] - : 0 <= j' < pj and 0 <= k' < pk and 0 <= j < pj and 0 <= k < pk } + [_lp_linchk_stmt_ = 0, j_, k_] -> [_lp_linchk_stmt = 1, j, k] + : 0 <= j_ < pj and 0 <= k_ < pk and 0 <= j < pj and 0 <= k < pk } """ diff --git a/loopy/schedule/checker/schedule.py b/loopy/schedule/checker/schedule.py index d9029bfdf..afa0b47a2 100644 --- a/loopy/schedule/checker/schedule.py +++ b/loopy/schedule/checker/schedule.py @@ -72,7 +72,7 @@ for par_level in [0, 1, 2]: LTAG_VAR_NAMES.append("%slid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) GTAG_VAR_NAMES.append("%sgid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) -BEFORE_MARK = "'" +BEFORE_MARK = "_" # }}} diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 6547bb50e..56854ae94 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1281,11 +1281,6 @@ def test_sios_and_schedules_with_vec_and_barriers(): def test_add_stmt_inst_dependency(): - lp.set_caching_enabled(False) - # TODO REMOVE THIS^ (prevents - # TypeError: unsupported type for persistent hash keying: - # ) during preprocessing - # Make kernel and use OLD deps to linearize correctly for now i_range_str = "0 <= i < pi" i_range_str_p = "0 <= i' < pi" From 31783ee367bfc306a782e9a8e4317c36382dda7e Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 9 Apr 2021 13:04:49 -0500 Subject: [PATCH 12/62] add todo --- loopy/schedule/checker/schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/schedule/checker/schedule.py b/loopy/schedule/checker/schedule.py index afa0b47a2..5444495a8 100644 --- a/loopy/schedule/checker/schedule.py +++ b/loopy/schedule/checker/schedule.py @@ -72,7 +72,7 @@ for par_level in [0, 1, 2]: LTAG_VAR_NAMES.append("%slid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) GTAG_VAR_NAMES.append("%sgid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) -BEFORE_MARK = "_" +BEFORE_MARK = "_" # TODO switch back to apostrophe after islpy is updated # }}} From f42f2ac5ab66e74b6b72c0de227b1f9cacc0c6e0 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 14 Apr 2021 09:26:48 -0500 Subject: [PATCH 13/62] switch BEFORE_MARK back to apostrophe now that isl can handle it during pickling --- loopy/schedule/checker/__init__.py | 4 ++-- loopy/schedule/checker/schedule.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 5811f3048..82f9cb5fc 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -85,8 +85,8 @@ def get_pairwise_statement_orderings( >>> print(str(sio_dict[("stmt_a", "stmt_b")].sio_intra_thread ... ).replace("{ ", "{\n").replace(" :", "\n:")) [pj, pk] -> { - [_lp_linchk_stmt_ = 0, j_, k_] -> [_lp_linchk_stmt = 1, j, k] - : 0 <= j_ < pj and 0 <= k_ < pk and 0 <= j < pj and 0 <= k < pk } + [_lp_linchk_stmt' = 0, j', k'] -> [_lp_linchk_stmt = 1, j, k] + : 0 <= j' < pj and 0 <= k' < pk and 0 <= j < pj and 0 <= k < pk } """ diff --git a/loopy/schedule/checker/schedule.py b/loopy/schedule/checker/schedule.py index 345257af0..726256b45 100644 --- a/loopy/schedule/checker/schedule.py +++ b/loopy/schedule/checker/schedule.py @@ -72,7 +72,7 @@ for par_level in [0, 1, 2]: LTAG_VAR_NAMES.append("%slid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) GTAG_VAR_NAMES.append("%sgid%d" % (LIN_CHECK_IDENTIFIER_PREFIX, par_level)) -BEFORE_MARK = "_" # TODO switch back to apostrophe after islpy is updated +BEFORE_MARK = "'" # }}} From a202ffe2d8caf6ad427f18532391264203d1f641 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 14 Apr 2021 09:45:37 -0500 Subject: [PATCH 14/62] fix doctest after projecting out unused inames from sched dims --- loopy/schedule/checker/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 82f9cb5fc..bbea06dca 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -85,8 +85,8 @@ def get_pairwise_statement_orderings( >>> print(str(sio_dict[("stmt_a", "stmt_b")].sio_intra_thread ... ).replace("{ ", "{\n").replace(" :", "\n:")) [pj, pk] -> { - [_lp_linchk_stmt' = 0, j', k'] -> [_lp_linchk_stmt = 1, j, k] - : 0 <= j' < pj and 0 <= k' < pk and 0 <= j < pj and 0 <= k < pk } + [_lp_linchk_stmt' = 0, j'] -> [_lp_linchk_stmt = 1, k] + : pj > 0 and pk > 0 and 0 <= j' < pj and 0 <= k < pk } """ From 5206882d0af1a1c7643792fd737282e195060247 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 14 Apr 2021 11:11:01 -0500 Subject: [PATCH 15/62] add test for dep checking with finite differences dependency example, including barrier handling --- test/test_linearization_checker.py | 121 ++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 35009d718..bee9cc988 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1285,7 +1285,7 @@ def test_sios_and_schedules_with_vec_and_barriers(): def test_add_stmt_inst_dependency(): - # Make kernel and use OLD deps to linearize correctly for now + # Make kernel and use OLD deps to control linearization order for now i_range_str = "0 <= i < pi" i_range_str_p = "0 <= i' < pi" assumptions_str = "pi >= 1" @@ -1392,7 +1392,124 @@ def test_add_stmt_inst_dependency(): assert not unsatisfied_deps -# TODO create more kernels with valid/invalid linearizations to test checker +def test_new_dependencies_finite_diff(): + + # Define kernel + knl = lp.make_kernel( + "[nx,nt] -> {[x, t]: 0<=x {{ [{0}'=0, x', t'] -> [{0}=0, x, t] : " + "((x = x' and t = t'+2) or " + " (x'-1 <= x <= x'+1 and t = t' + 1)) and " + "{1} and {2} }}".format( + STATEMENT_VAR_NAME, + xt_range_str, + xt_range_str_p, + )) + knl = lp.add_stmt_inst_dependency(knl, "stmt", "stmt", dep) + + ref_knl = knl + + # {{{ Check with corrct loop nest order + + # Prioritize loops correctly + knl = lp.prioritize_loops(knl, "t,x") + + # Make sure deps are satisfied + proc_knl = preprocess_kernel(knl) + lin_knl = get_one_linearized_kernel(proc_knl) + lin_items = lin_knl.linearization + + unsatisfied_deps = lp.find_unsatisfied_dependencies( + proc_knl, lin_items) + + print(lp.generate_code_v2(lin_knl).device_code()) + assert not unsatisfied_deps + + # }}} + # {{{ Check with incorrect loop nest order + + # Now prioritize loops incorrectly + knl = ref_knl + knl = lp.prioritize_loops(knl, "x,t") + + # Make sure unsatisfied deps are caught + proc_knl = preprocess_kernel(knl) + lin_knl = get_one_linearized_kernel(proc_knl) + lin_items = lin_knl.linearization + + unsatisfied_deps = lp.find_unsatisfied_dependencies( + proc_knl, lin_items) + + print(lp.generate_code_v2(lin_knl).device_code()) + assert len(unsatisfied_deps) == 1 + + # }}} + # {{{ Check with parallel x and no barrier + + # Parallelize the x loop + knl = ref_knl + knl = lp.prioritize_loops(knl, "t,x") + knl = lp.tag_inames(knl, "x:l.0") + + # Make sure unsatisfied deps are caught + proc_knl = preprocess_kernel(knl) + lin_knl = get_one_linearized_kernel(proc_knl) + lin_items = lin_knl.linearization + + # Without a barrier, deps not satisfied + # Make sure there is no barrier, and that unsatisfied deps are caught + from loopy.schedule import Barrier + print(lp.generate_code_v2(lin_knl).device_code()) + for lin_item in lin_items: + assert not isinstance(lin_item, Barrier) + + unsatisfied_deps = lp.find_unsatisfied_dependencies( + proc_knl, lin_items) + + assert len(unsatisfied_deps) == 1 + + # }}} + # {{{ Check with parallel x and included barrier + + # Insert a barrier to satisfy deps + knl = lp.make_kernel( + "[nx,nt] -> {[x, t]: 0<=x= 1 and nx >= 1") + # knl = lp.tag_inames(knl, "x_outer:g.0, x_inner:l.0") if __name__ == "__main__": From 1b61319212420a93ab37a6af9b1f2163ecbb303d Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 15 Apr 2021 11:29:14 -0500 Subject: [PATCH 16/62] add docstring for find_unsatisfied_dependencies() --- loopy/schedule/checker/__init__.py | 44 ++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index bbea06dca..a343a70c9 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -142,10 +142,48 @@ def get_pairwise_statement_orderings( def find_unsatisfied_dependencies( knl, - linearization_items, + lin_items, ): + """For each statement (:class:`loopy.InstructionBase`) found in a + preprocessed kernel, determine which dependencies, if any, have been + violated by the linearization described by `lin_items`, and return these + dependencies. + + :arg knl: A preprocessed (or linearized) :class:`loopy.kernel.LoopKernel` + containing the statements (:class:`loopy.InstructionBase`) whose + dependencies will be checked against the linearization items. + + :arg lin_items: A list of :class:`loopy.schedule.ScheduleItem` + (to be renamed to `loopy.schedule.LinearizationItem`) containing all + linearization items in `knl.linearization`. To allow usage of + this routine during linearization, a truncated (i.e. partial) + linearization may be passed through this argument. - # TODO document + :returns: A list of unsatisfied dependencies, each described using a + :class:`collections.namedtuple` containing the following: + + - `statement_pair`: The (before, after) pair of statement IDs involved + in the dependency. + - `dependency`: An class:`islpy.Map` from each instance of the first + statement to all instances of the second statement that must occur + later. + - `statement_ordering`: A statement ordering information tuple + resulting from `lp.get_pairwise_statement_orderings`, a + :class:`collections.namedtuple` containing the intra-thread + statement instance ordering (SIO) (`sio_intra_thread`), + intra-group SIO (`sio_intra_group`), and global + SIO (`sio_global`), each realized as an :class:`islpy.Map` from each + instance of the first statement to all instances of the second + statement that occur later, as well as the intra-thread pairwise + schedule (`pwsched_intra_thread`), intra-group pairwise schedule + (`pwsched_intra_group`), and the global pairwise schedule + (`pwsched_global`), each containing a pair of mappings from statement + instances to points in a lexicographic ordering, one for each + statement. Note that a pairwise schedule alone cannot be used to + reproduce the corresponding SIO without the corresponding (unique) + lexicographic order map, which is not returned. + + """ # {{{ make sure kernel has been preprocessed @@ -182,7 +220,7 @@ def find_unsatisfied_dependencies( pworders = get_pairwise_statement_orderings( knl, - linearization_items, + lin_items, stmt_pairs_to_deps.keys(), ) From 00d973735ec3ea0a07275d288d65b8a24ec08d03 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 15 Apr 2021 11:38:27 -0500 Subject: [PATCH 17/62] add docstring to add_stmt_inst_dependency() --- loopy/transform/instruction.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 703d1d243..73bc9ba13 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -121,8 +121,22 @@ def add_dep(insn): def add_stmt_inst_dependency( kernel, stmt_id, depends_on_id, new_dependency): - """Add the statement instance dependency *new_dependency* to statement with - id *stmt_id*. + """Add the statement instance dependency `new_dependency` to the statement with + id `stmt_id`. + + :arg kernel: A :class:`loopy.kernel.LoopKernel`. + + :arg stmt_id: The :class:`str` statement identifier of the statement to + which the dependency will be added. + + :arg depends_on_id: The :class:`str` identifier of the statement that is + depended on, i.e., the statement with statement instances that must + happen before those of `stmt_id`. + + :arg new_dependency: An class:`islpy.Map` from each instance of the first + statement to all instances of the second statement that must occur + later. + """ if stmt_id not in kernel.id_to_insn: From 887f64e9847cd9b3d2480e6f8d89667f4097f6f4 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 15 Apr 2021 11:53:39 -0500 Subject: [PATCH 18/62] add dependencies attribute to docstring for InstructionBase; handle dependencies in InstructionBase.get_str_options() --- loopy/kernel/instruction.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 951d9975c..7933430ee 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -75,7 +75,7 @@ class UseStreamingStoreTag(Tag): # {{{ instructions: base class class InstructionBase(ImmutableRecord, Taggable): - """A base class for all types of instruction that can occur in + r"""A base class for all types of instruction that can occur in a kernel. .. attribute:: id @@ -87,7 +87,7 @@ class InstructionBase(ImmutableRecord, Taggable): .. attribute:: depends_on - a :class:`frozenset` of :attr:`id` values of :class:`InstructionBase` + A :class:`frozenset` of :attr:`id` values of :class:`InstructionBase` instances that *must* be executed before this one. Note that :func:`loopy.preprocess_kernel` (usually invoked automatically) augments this by adding dependencies on any writes to temporaries read @@ -106,6 +106,15 @@ class InstructionBase(ImmutableRecord, Taggable): :func:`loopy.make_kernel`. Note, that this is not meant as a user-facing interface. + .. attribute:: dependencies + + A :class:`dict` mapping :attr:`id` values of :class:`InstructionBase` + instances (each referring to a statement with statement instances that + must be executed before instances of this statement) to lists (one list + per key) of class:`islpy.Map`\ s mapping each instance of the dependee + statement to all instances of this statement that must occur later. Note + that this dict will eventually replace the `depends_on` attribute. + .. attribute:: depends_on_is_final A :class:`bool` determining whether :attr:`depends_on` constitutes @@ -393,7 +402,8 @@ def get_str_options(self): if self.depends_on: result.append("dep="+":".join(self.depends_on)) - # TODO something with dependencies? + if self.dependencies: + result.append("dependencies="+":".join(self.dependencies.keys())) if self.no_sync_with: result.append("nosync="+":".join( "%s@%s" % entry for entry in self.no_sync_with)) From bc1c27b8f8ed73a7fcd0b1fc4669f9d4ad1237bc Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 23 Apr 2021 17:58:04 -0500 Subject: [PATCH 19/62] rename add_stmt_inst_dependency->add_dependency_v2 --- loopy/__init__.py | 4 ++-- loopy/transform/instruction.py | 4 ++-- test/test_linearization_checker.py | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index f94cbe021..36196206b 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -79,7 +79,7 @@ from loopy.transform.instruction import ( find_instructions, map_instructions, set_instruction_priority, - add_dependency, add_stmt_inst_dependency, + add_dependency, add_dependency_v2, remove_instructions, replace_instruction_ids, tag_instructions, @@ -206,7 +206,7 @@ "find_instructions", "map_instructions", "set_instruction_priority", - "add_dependency", "add_stmt_inst_dependency", + "add_dependency", "add_dependency_v2", "remove_instructions", "replace_instruction_ids", "tag_instructions", diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 73bc9ba13..8b539ca5f 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -117,9 +117,9 @@ def add_dep(insn): # }}} -# {{{ add_stmt_inst_dependency +# {{{ add_dependency_v2 -def add_stmt_inst_dependency( +def add_dependency_v2( kernel, stmt_id, depends_on_id, new_dependency): """Add the statement instance dependency `new_dependency` to the statement with id `stmt_id`. diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 3ed214bf6..3192688ec 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1342,9 +1342,9 @@ def test_sios_with_matmul(): # {{{ Dependency tests -# {{{ test_add_stmt_inst_dependency +# {{{ test_add_dependency_v2 -def test_add_stmt_inst_dependency(): +def test_add_dependency_v2(): # Make kernel and use OLD deps to control linearization order for now i_range_str = "0 <= i < pi" @@ -1377,7 +1377,7 @@ def test_add_stmt_inst_dependency(): assumptions_str, )) - knl = lp.add_stmt_inst_dependency(knl, "stmt_b", "stmt_a", dep_b_on_a) + knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) for stmt in knl.instructions: if stmt.id == "stmt_b": @@ -1397,7 +1397,7 @@ def test_add_stmt_inst_dependency(): assumptions_str, )) - knl = lp.add_stmt_inst_dependency(knl, "stmt_b", "stmt_a", dep_b_on_a_2) + knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) for stmt in knl.instructions: if stmt.id == "stmt_b": @@ -1426,8 +1426,8 @@ def test_add_stmt_inst_dependency(): assumptions_str, )) - knl = lp.add_stmt_inst_dependency(knl, "stmt_c", "stmt_a", dep_c_on_a) - knl = lp.add_stmt_inst_dependency(knl, "stmt_c", "stmt_b", dep_c_on_b) + knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_a", dep_c_on_a) + knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_b", dep_c_on_b) for stmt in knl.instructions: if stmt.id == "stmt_b": @@ -1479,7 +1479,7 @@ def test_new_dependencies_finite_diff(): xt_range_str, xt_range_str_p, )) - knl = lp.add_stmt_inst_dependency(knl, "stmt", "stmt", dep) + knl = lp.add_dependency_v2(knl, "stmt", "stmt", dep) ref_knl = knl From 1615991b9764efd6a4d0d39700aa952c595ab618 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sat, 24 Apr 2021 23:27:02 -0500 Subject: [PATCH 20/62] reduce duplicated code in tests by using _process_and_linearize(knl) function (created in ancestor branch) --- test/test_linearization_checker.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index fd01cc7d6..c5f3692ec 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1435,9 +1435,7 @@ def test_add_dependency_v2(): assert not stmt.dependencies # Now make sure deps are satisfied - proc_knl = preprocess_kernel(knl) - lin_knl = get_one_linearized_kernel(proc_knl) - lin_items = lin_knl.linearization + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) @@ -1481,9 +1479,7 @@ def test_new_dependencies_finite_diff(): knl = lp.prioritize_loops(knl, "t,x") # Make sure deps are satisfied - proc_knl = preprocess_kernel(knl) - lin_knl = get_one_linearized_kernel(proc_knl) - lin_items = lin_knl.linearization + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) @@ -1499,9 +1495,7 @@ def test_new_dependencies_finite_diff(): knl = lp.prioritize_loops(knl, "x,t") # Make sure unsatisfied deps are caught - proc_knl = preprocess_kernel(knl) - lin_knl = get_one_linearized_kernel(proc_knl) - lin_items = lin_knl.linearization + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) @@ -1518,9 +1512,7 @@ def test_new_dependencies_finite_diff(): knl = lp.tag_inames(knl, "x:l.0") # Make sure unsatisfied deps are caught - proc_knl = preprocess_kernel(knl) - lin_knl = get_one_linearized_kernel(proc_knl) - lin_items = lin_knl.linearization + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) # Without a barrier, deps not satisfied # Make sure there is no barrier, and that unsatisfied deps are caught @@ -1551,9 +1543,7 @@ def test_new_dependencies_finite_diff(): knl, {"u": np.float32, "dx": np.float32, "dt": np.float32}) # Make sure deps are satisfied - proc_knl = preprocess_kernel(knl) - lin_knl = get_one_linearized_kernel(proc_knl) - lin_items = lin_knl.linearization + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) print(lp.generate_code_v2(lin_knl).device_code()) unsatisfied_deps = lp.find_unsatisfied_dependencies( From 48b4dfbf26c14a06ae0ea328e1afff274e57cc03 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sun, 23 May 2021 17:04:24 -0500 Subject: [PATCH 21/62] make helper function for dep creation that adds marks and inserts statement var dims (make_dep_map) --- loopy/schedule/checker/utils.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 401fd477a..86bf935a4 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -136,6 +136,51 @@ def append_mark_to_strings(strings, mark): return [s+mark for s in strings] +# {{{ make_dep_map + +def make_dep_map(s, self_dep=False): + + # TODO put this function in the right place + + from loopy.schedule.checker.schedule import ( + BEFORE_MARK, + STATEMENT_VAR_NAME, + ) + + map_init = isl.Map(s) + + # TODO something smarter than this assert + for dim_name in map_init.get_var_names(dt.in_): + assert BEFORE_MARK not in dim_name + + # append BEFORE_MARK to in-vars + map_marked = append_mark_to_isl_map_var_names( + map_init, dt.in_, BEFORE_MARK) + + # insert statement dims: + map_with_stmts = insert_and_name_isl_dims( + map_marked, dt.in_, [STATEMENT_VAR_NAME+BEFORE_MARK], 0) + map_with_stmts = insert_and_name_isl_dims( + map_with_stmts, dt.out, [STATEMENT_VAR_NAME], 0) + + # assign values 0 or 1 to statement dims + sid_after = 0 if self_dep else 1 + + map_with_stmts = map_with_stmts.add_constraint( + isl.Constraint.eq_from_names( + map_with_stmts.space, + {1: 0, STATEMENT_VAR_NAME+BEFORE_MARK: -1})) + + map_with_stmts = map_with_stmts.add_constraint( + isl.Constraint.eq_from_names( + map_with_stmts.space, + {1: sid_after, STATEMENT_VAR_NAME: -1})) + + return map_with_stmts + +# }}} + + def sorted_union_of_names_in_isl_sets( isl_sets, set_dim=dt.set): From 36793bd86ae5e89e9f7c751e0745987bd076dec9 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sun, 23 May 2021 17:05:05 -0500 Subject: [PATCH 22/62] use make_dep_map() instead of _isl_map_with_marked_dims (still need to replace more) --- test/test_linearization_checker.py | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index c5f3692ec..fab930675 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -45,6 +45,7 @@ ) from loopy.schedule.checker.utils import ( ensure_dim_names_match_and_align, + make_dep_map, ) logger = logging.getLogger(__name__) @@ -1360,14 +1361,14 @@ def test_add_dependency_v2(): assert not stmt.dependencies # Add a dependency to stmt_b - dep_b_on_a = _isl_map_with_marked_dims( - "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i > i' " - "and {1} and {2} and {3} }}".format( - STATEMENT_VAR_NAME, + dep_b_on_a = make_dep_map( + "[pi] -> {{ [i'] -> [i] : i > i' " + "and {0} and {1} and {2} }}".format( i_range_str, i_range_str_p, assumptions_str, - )) + ), + self_dep=False) knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) @@ -1380,14 +1381,14 @@ def test_add_dependency_v2(): assert not stmt.dependencies # Add a second dependency to stmt_b - dep_b_on_a_2 = _isl_map_with_marked_dims( - "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i = i' " - "and {1} and {2} and {3} }}".format( - STATEMENT_VAR_NAME, + dep_b_on_a_2 = make_dep_map( + "[pi] -> {{ [i'] -> [i] : i = i' " + "and {0} and {1} and {2} }}".format( i_range_str, i_range_str_p, assumptions_str, - )) + ), + self_dep=False) knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) @@ -1400,6 +1401,7 @@ def test_add_dependency_v2(): assert not stmt.dependencies # Add dependencies to stmt_c + # TODO use make_dep_map instead of _isl_map_with_marked_dims where possible dep_c_on_a = _isl_map_with_marked_dims( "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i >= i' " @@ -1460,15 +1462,16 @@ def test_new_dependencies_finite_diff(): # Define dependency xt_range_str = "0 <= x < nx and 0 <= t < nt" xt_range_str_p = "0 <= x' < nx and 0 <= t' < nt" - dep = _isl_map_with_marked_dims( - "[nx,nt] -> {{ [{0}'=0, x', t'] -> [{0}=0, x, t] : " + dep = make_dep_map( + "[nx,nt] -> {{ [x', t'] -> [x, t] : " "((x = x' and t = t'+2) or " " (x'-1 <= x <= x'+1 and t = t' + 1)) and " - "{1} and {2} }}".format( - STATEMENT_VAR_NAME, + "{0} and {1} }}".format( xt_range_str, xt_range_str_p, - )) + ), + self_dep=True) + knl = lp.add_dependency_v2(knl, "stmt", "stmt", dep) ref_knl = knl From ec3185d3edd08d7cac12463a0479bd092ba80da7 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 26 May 2021 20:32:27 -0500 Subject: [PATCH 23/62] allow dep checking to work if lin_items is not provided --- loopy/schedule/checker/__init__.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index a343a70c9..99e3ffb90 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -142,7 +142,7 @@ def get_pairwise_statement_orderings( def find_unsatisfied_dependencies( knl, - lin_items, + lin_items=None, ): """For each statement (:class:`loopy.InstructionBase`) found in a preprocessed kernel, determine which dependencies, if any, have been @@ -157,7 +157,8 @@ def find_unsatisfied_dependencies( (to be renamed to `loopy.schedule.LinearizationItem`) containing all linearization items in `knl.linearization`. To allow usage of this routine during linearization, a truncated (i.e. partial) - linearization may be passed through this argument. + linearization may be passed through this argument. If not provided, + `knl.linearization` will be used. :returns: A list of unsatisfied dependencies, each described using a :class:`collections.namedtuple` containing the following: @@ -185,13 +186,17 @@ def find_unsatisfied_dependencies( """ - # {{{ make sure kernel has been preprocessed + # {{{ Handle lin_items=None and make sure kernel has been preprocessed - # Note: kernels must always be preprocessed before scheduling from loopy.kernel import KernelState - assert knl.state in [ - KernelState.PREPROCESSED, - KernelState.LINEARIZED] + if lin_items is None: + assert knl.state == KernelState.LINEARIZED + lin_items = knl.linearization + else: + # Note: kernels must always be preprocessed before scheduling + assert knl.state in [ + KernelState.PREPROCESSED, + KernelState.LINEARIZED] # }}} From f17f2825064e06f01944fce4f55a66ab94cc1bb9 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 26 May 2021 20:33:03 -0500 Subject: [PATCH 24/62] make sure dep checking works even when linearization items are not provided separately from kernel --- test/test_linearization_checker.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index fab930675..0bddbc973 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1444,6 +1444,11 @@ def test_add_dependency_v2(): assert not unsatisfied_deps + # Make sure dep checking also works with just linearized kernel + unsatisfied_deps = lp.find_unsatisfied_dependencies(lin_knl) + + assert not unsatisfied_deps + # }}} @@ -1490,6 +1495,11 @@ def test_new_dependencies_finite_diff(): print(lp.generate_code_v2(lin_knl).device_code()) assert not unsatisfied_deps + # Make sure dep checking also works with just linearized kernel + unsatisfied_deps = lp.find_unsatisfied_dependencies(lin_knl) + + assert not unsatisfied_deps + # }}} # {{{ Check with incorrect loop nest order From 77bed8f39563822d97b5c5dea1071928d50401dd Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 2 Jun 2021 16:03:32 -0500 Subject: [PATCH 25/62] add TODO --- loopy/transform/instruction.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 8b539ca5f..01dd95d84 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -138,6 +138,8 @@ def add_dependency_v2( later. """ + # TODO make this accept multiple deps and/or multiple stmts so that + # these can be added in fewer passes through the instructions if stmt_id not in kernel.id_to_insn: raise LoopyError("no instructions found matching '%s'," From 493c8e8f707d5388dd93cfadd90108a9013e99ce Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 2 Jun 2021 16:25:51 -0500 Subject: [PATCH 26/62] make make_dep_map smart enough to get relevant iname domains from kernel if provided --- loopy/schedule/checker/utils.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 86bf935a4..152dc56a1 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -138,7 +138,7 @@ def append_mark_to_strings(strings, mark): # {{{ make_dep_map -def make_dep_map(s, self_dep=False): +def make_dep_map(s, self_dep=False, knl_with_domains=None): # TODO put this function in the right place @@ -176,6 +176,23 @@ def make_dep_map(s, self_dep=False): map_with_stmts.space, {1: sid_after, STATEMENT_VAR_NAME: -1})) + if knl_with_domains is not None: + # intersect map with knl domains + inames = map_init.get_var_names(dt.out) + inames_dom = knl_with_domains.get_inames_domain( + inames).project_out_except(inames, [dt.set]) + inames_dom_marked = append_mark_to_isl_map_var_names( + inames_dom, dt.set, BEFORE_MARK) + + inames_dom_aligned = isl.align_spaces( + inames_dom, map_with_stmts.range()) + inames_dom_marked_aligned = isl.align_spaces( + inames_dom_marked, map_with_stmts.domain()) + + map_with_stmts = map_with_stmts.intersect_range( + inames_dom_aligned + ).intersect_domain(inames_dom_marked_aligned) + return map_with_stmts # }}} From d7e934248a0ea03b017570cacb9d8a8454a6ac94 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 2 Jun 2021 16:26:20 -0500 Subject: [PATCH 27/62] some initial tests for make_dep_map --- test/test_linearization_checker.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 0bddbc973..f46c0abd1 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1363,12 +1363,19 @@ def test_add_dependency_v2(): # Add a dependency to stmt_b dep_b_on_a = make_dep_map( "[pi] -> {{ [i'] -> [i] : i > i' " + "and {0} }}".format(assumptions_str), + self_dep=False, knl_with_domains=knl) + + # test make_dep_map while we're here: + dep_b_on_a_test = _isl_map_with_marked_dims( + "[pi] -> {{ [{3}'=0, i'] -> [{3}=1, i] : i > i' " "and {0} and {1} and {2} }}".format( i_range_str, i_range_str_p, assumptions_str, - ), - self_dep=False) + STATEMENT_VAR_NAME, + )) + _align_and_compare_maps([(dep_b_on_a, dep_b_on_a_test)]) knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) @@ -1383,12 +1390,19 @@ def test_add_dependency_v2(): # Add a second dependency to stmt_b dep_b_on_a_2 = make_dep_map( "[pi] -> {{ [i'] -> [i] : i = i' " + "and {0}}}".format(assumptions_str), + self_dep=False, knl_with_domains=knl) + + # test make_dep_map while we're here: + dep_b_on_a_2_test = _isl_map_with_marked_dims( + "[pi] -> {{ [{3}'=0, i'] -> [{3}=1, i] : i = i' " "and {0} and {1} and {2} }}".format( i_range_str, i_range_str_p, assumptions_str, - ), - self_dep=False) + STATEMENT_VAR_NAME, + )) + _align_and_compare_maps([(dep_b_on_a_2, dep_b_on_a_2_test)]) knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) From fb4c43d02eb0e3151dd791db3002531cb9337184 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sat, 5 Jun 2021 21:06:48 -0500 Subject: [PATCH 28/62] fix make_dep_map so it handles automatic domain creation correctly when input dims don't match output dims --- loopy/schedule/checker/utils.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 152dc56a1..85cc9ec59 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -178,20 +178,26 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): if knl_with_domains is not None: # intersect map with knl domains - inames = map_init.get_var_names(dt.out) - inames_dom = knl_with_domains.get_inames_domain( - inames).project_out_except(inames, [dt.set]) - inames_dom_marked = append_mark_to_isl_map_var_names( - inames_dom, dt.set, BEFORE_MARK) + inames_in = map_init.get_var_names(dt.in_) + inames_out = map_init.get_var_names(dt.out) - inames_dom_aligned = isl.align_spaces( - inames_dom, map_with_stmts.range()) - inames_dom_marked_aligned = isl.align_spaces( - inames_dom_marked, map_with_stmts.domain()) + inames_in_dom = knl_with_domains.get_inames_domain( + inames_in).project_out_except(inames_in, [dt.set]) + inames_out_dom = knl_with_domains.get_inames_domain( + inames_out).project_out_except(inames_out, [dt.set]) + + # mark dependee inames + inames_in_dom_marked = append_mark_to_isl_map_var_names( + inames_in_dom, dt.set, BEFORE_MARK) + + inames_in_dom_marked_aligned = isl.align_spaces( + inames_in_dom_marked, map_with_stmts.domain()) + inames_out_dom_aligned = isl.align_spaces( + inames_out_dom, map_with_stmts.range()) map_with_stmts = map_with_stmts.intersect_range( - inames_dom_aligned - ).intersect_domain(inames_dom_marked_aligned) + inames_out_dom_aligned + ).intersect_domain(inames_in_dom_marked_aligned) return map_with_stmts From 473116c19f6c8c686052e13d235c2e6ea25e0608 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Sat, 5 Jun 2021 21:07:09 -0500 Subject: [PATCH 29/62] add dedicated test for make_dep_map --- test/test_linearization_checker.py | 50 ++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index f46c0abd1..013ded908 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1466,6 +1466,56 @@ def test_add_dependency_v2(): # }}} +# {{{ test_make_dep_map + +def test_make_dep_map(): + # This is also tested inside other test functions, but + # here we specifically test case where the statement inames + # don't match + + # Make kernel and use OLD deps to control linearization order for now + i_range_str = "0 <= i < n" + i_range_str_p = "0 <= i' < n" + j_range_str = "0 <= j < n" + j_range_str_p = "0 <= j' < n" + k_range_str = "0 <= k < n" + # k_range_str_p = "0 <= k' < n" # (not used) + knl = lp.make_kernel( + "{[i,j,k]: %s}" % (" and ".join([i_range_str, j_range_str, k_range_str])), + """ + a[i,j] = 3.14 {id=stmt_a} + b[k] = a[i,k] {id=stmt_b, dep=stmt_a} + """, + name="example", + lang_version=(2018, 2) + ) + knl = lp.add_and_infer_dtypes(knl, {"a,b": np.float32}) + + for stmt in knl.instructions: + assert not stmt.dependencies + + # Add a dependency to stmt_b + dep_b_on_a = make_dep_map( + "[n] -> { [i',j'] -> [i,k] : i > i' and j' < k}", + self_dep=False, knl_with_domains=knl) + + # Create expected dep + dep_b_on_a_test = _isl_map_with_marked_dims( + "[n] -> {{ [{0}'=0, i', j'] -> [{0}=1, i, k] : i > i' and j' < k" + " and {1} }}".format( + STATEMENT_VAR_NAME, + " and ".join([ + i_range_str, + i_range_str_p, + j_range_str_p, + k_range_str, + ]) + )) + _align_and_compare_maps([(dep_b_on_a, dep_b_on_a_test)]) + +# }}} + + # {{{ test_new_dependencies_finite_diff: def test_new_dependencies_finite_diff(): From 871ccaeed9bafcd8ba99e490ea264456a7e9916b Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 10 Jun 2021 16:42:38 -0500 Subject: [PATCH 30/62] don't check deps on/by barriers at the moment --- loopy/schedule/checker/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 99e3ffb90..8c0ab9c52 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -214,11 +214,16 @@ def find_unsatisfied_dependencies( # (stmt_id_before2, stmt_id_after2): [dep1, dep2, ...], # ...} + from loopy.kernel.instruction import BarrierInstruction + # TODO (fix) for now, don't check deps on/by barriers for stmt_after in knl.instructions: - for before_id, dep_list in stmt_after.dependencies.items(): - # (don't compare dep maps to maps found; duplicate deps should be rare) - stmt_pairs_to_deps.setdefault( - (before_id, stmt_after.id), []).extend(dep_list) + if not isinstance(stmt_after, BarrierInstruction): + for before_id, dep_list in stmt_after.dependencies.items(): + if not isinstance(knl.id_to_insn[before_id], BarrierInstruction): + # (don't compare dep maps to maps found; + # duplicate deps should be rare) + stmt_pairs_to_deps.setdefault( + (before_id, stmt_after.id), []).extend(dep_list) # }}} # {{{ Get statement instance orderings From a8ec66d7357c494214f94140c92ba9ce2c67ab52 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 10 Jun 2021 18:07:05 -0500 Subject: [PATCH 31/62] set obj_bigger_ok=True when aligning inames domain with dep in make_dep_map --- loopy/schedule/checker/utils.py | 7 +++++-- loopy/target/c/compyte | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 85cc9ec59..4bba569e8 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -190,10 +190,13 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): inames_in_dom_marked = append_mark_to_isl_map_var_names( inames_in_dom, dt.set, BEFORE_MARK) + # align spaces adds the stmt var inames_in_dom_marked_aligned = isl.align_spaces( - inames_in_dom_marked, map_with_stmts.domain()) + inames_in_dom_marked, map_with_stmts.domain(), + obj_bigger_ok=True) # e.g., params might exist inames_out_dom_aligned = isl.align_spaces( - inames_out_dom, map_with_stmts.range()) + inames_out_dom, map_with_stmts.range(), + obj_bigger_ok=True) # e.g., params might exist map_with_stmts = map_with_stmts.intersect_range( inames_out_dom_aligned diff --git a/loopy/target/c/compyte b/loopy/target/c/compyte index 71bffa1ae..7e48e1166 160000 --- a/loopy/target/c/compyte +++ b/loopy/target/c/compyte @@ -1 +1 @@ -Subproject commit 71bffa1ae64ed98b9d922c79a6f9cc7eb4fd642f +Subproject commit 7e48e1166a13cfbb7b60f909b071f088034ffda1 From 31718ae0e9240e865c7d20cab563d81071850176 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 15 Jul 2021 16:50:37 -0500 Subject: [PATCH 32/62] after callables update, use knl[loopy_kernel] where needed --- test/test_linearization_checker.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index ea1cff04f..012a73458 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1345,21 +1345,19 @@ def test_add_dependency_v2(): b[i] = a[i] {id=stmt_b, dep=stmt_a} c[i] = b[i] {id=stmt_c, dep=stmt_b} """, - name="example", - assumptions=assumptions_str, lang_version=(2018, 2) ) knl = lp.add_and_infer_dtypes( knl, {"a": np.float32, "b": np.float32, "c": np.float32}) - for stmt in knl.instructions: + for stmt in knl["loopy_kernel"].instructions: assert not stmt.dependencies # Add a dependency to stmt_b dep_b_on_a = make_dep_map( "[pi] -> {{ [i'] -> [i] : i > i' " "and {0} }}".format(assumptions_str), - self_dep=False, knl_with_domains=knl) + self_dep=False, knl_with_domains=knl["loopy_kernel"]) # test make_dep_map while we're here: dep_b_on_a_test = _isl_map_with_marked_dims( @@ -1374,7 +1372,7 @@ def test_add_dependency_v2(): knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) - for stmt in knl.instructions: + for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": assert stmt.dependencies == { "stmt_a": [dep_b_on_a, ], @@ -1386,7 +1384,7 @@ def test_add_dependency_v2(): dep_b_on_a_2 = make_dep_map( "[pi] -> {{ [i'] -> [i] : i = i' " "and {0}}}".format(assumptions_str), - self_dep=False, knl_with_domains=knl) + self_dep=False, knl_with_domains=knl["loopy_kernel"]) # test make_dep_map while we're here: dep_b_on_a_2_test = _isl_map_with_marked_dims( @@ -1401,7 +1399,7 @@ def test_add_dependency_v2(): knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) - for stmt in knl.instructions: + for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": assert stmt.dependencies == { "stmt_a": [dep_b_on_a, dep_b_on_a_2], @@ -1432,7 +1430,7 @@ def test_add_dependency_v2(): knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_a", dep_c_on_a) knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_b", dep_c_on_b) - for stmt in knl.instructions: + for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": assert stmt.dependencies == { "stmt_a": [dep_b_on_a, dep_b_on_a_2], @@ -1481,18 +1479,17 @@ def test_make_dep_map(): a[i,j] = 3.14 {id=stmt_a} b[k] = a[i,k] {id=stmt_b, dep=stmt_a} """, - name="example", lang_version=(2018, 2) ) knl = lp.add_and_infer_dtypes(knl, {"a,b": np.float32}) - for stmt in knl.instructions: + for stmt in knl["loopy_kernel"].instructions: assert not stmt.dependencies # Add a dependency to stmt_b dep_b_on_a = make_dep_map( "[n] -> { [i',j'] -> [i,k] : i > i' and j' < k}", - self_dep=False, knl_with_domains=knl) + self_dep=False, knl_with_domains=knl["loopy_kernel"]) # Create expected dep dep_b_on_a_test = _isl_map_with_marked_dims( @@ -1551,7 +1548,6 @@ def test_new_dependencies_finite_diff(): unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) - print(lp.generate_code_v2(lin_knl).device_code()) assert not unsatisfied_deps # Make sure dep checking also works with just linearized kernel @@ -1572,7 +1568,6 @@ def test_new_dependencies_finite_diff(): unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) - print(lp.generate_code_v2(lin_knl).device_code()) assert len(unsatisfied_deps) == 1 # }}} @@ -1589,7 +1584,6 @@ def test_new_dependencies_finite_diff(): # Without a barrier, deps not satisfied # Make sure there is no barrier, and that unsatisfied deps are caught from loopy.schedule import Barrier - print(lp.generate_code_v2(lin_knl).device_code()) for lin_item in lin_items: assert not isinstance(lin_item, Barrier) @@ -1616,7 +1610,6 @@ def test_new_dependencies_finite_diff(): # Make sure deps are satisfied lin_items, proc_knl, lin_knl = _process_and_linearize(knl) - print(lp.generate_code_v2(lin_knl).device_code()) unsatisfied_deps = lp.find_unsatisfied_dependencies( proc_knl, lin_items) From 150576a392da79fd0c4f1ae2227e92c3b6647243 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 15 Jul 2021 16:51:08 -0500 Subject: [PATCH 33/62] use @for_each_kernel with add_dependency_v2 --- loopy/transform/instruction.py | 1 + 1 file changed, 1 insertion(+) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 11b93c386..5b5934da4 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -146,6 +146,7 @@ def add_dep(insn): # {{{ add_dependency_v2 +@for_each_kernel def add_dependency_v2( kernel, stmt_id, depends_on_id, new_dependency): """Add the statement instance dependency `new_dependency` to the statement with From d94cfa44da671784b9e71c6514429d24fc05f43d Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 26 Jul 2021 17:57:44 -0500 Subject: [PATCH 34/62] for some reason, I need to commit 'changes' to the submodule (but didn't actually do anything to submodule...?) --- loopy/target/c/compyte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/target/c/compyte b/loopy/target/c/compyte index 7e48e1166..71bffa1ae 160000 --- a/loopy/target/c/compyte +++ b/loopy/target/c/compyte @@ -1 +1 @@ -Subproject commit 7e48e1166a13cfbb7b60f909b071f088034ffda1 +Subproject commit 71bffa1ae64ed98b9d922c79a6f9cc7eb4fd642f From 215fc1c318a89f95405bebf26e2be537ba742572 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 15:35:01 -0500 Subject: [PATCH 35/62] clarify docstring for new dependencies --- loopy/kernel/instruction.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index c19966c7f..ebafb9d5e 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -109,11 +109,13 @@ class InstructionBase(ImmutableRecord, Taggable): .. attribute:: dependencies A :class:`dict` mapping :attr:`id` values of :class:`InstructionBase` - instances (each referring to a statement with statement instances that - must be executed before instances of this statement) to lists (one list - per key) of class:`islpy.Map`\ s mapping each instance of the dependee - statement to all instances of this statement that must occur later. Note - that this dict will eventually replace the `depends_on` attribute. + instances, each referring to a dependee statement (i.e., a statement + with statement instances that must be executed before instances of this + statement), to lists (one list per key) of class:`islpy.Map`\ s that + express dependency relationships by mapping each instance of the + dependee statement to all instances of this statement that must occur + later. This dict expresses the new statement-instance-level + dependencies and will eventually replace the `depends_on` attribute. .. attribute:: depends_on_is_final @@ -477,7 +479,7 @@ def __setstate__(self, val): if self.id is not None: # pylint:disable=access-member-before-definition self.id = intern(self.id) self.depends_on = intern_frozenset_of_ids(self.depends_on) - # TODO something with dependencies? + # FIXME Do something with self.dependencies? self.groups = intern_frozenset_of_ids(self.groups) self.conflicts_with_groups = ( intern_frozenset_of_ids(self.conflicts_with_groups)) @@ -1350,20 +1352,14 @@ class CInstruction(InstructionBase): def __init__(self, iname_exprs, code, - read_variables=frozenset(), - assignees=tuple(), - id=None, - depends_on=None, - depends_on_is_final=None, + read_variables=frozenset(), assignees=tuple(), + id=None, depends_on=None, depends_on_is_final=None, dependencies=None, - groups=None, - conflicts_with_groups=None, + groups=None, conflicts_with_groups=None, no_sync_with=None, - within_inames_is_final=None, - within_inames=None, + within_inames_is_final=None, within_inames=None, priority=0, - predicates=frozenset(), - tags=None): + predicates=frozenset(), tags=None): """ :arg iname_exprs: Like :attr:`iname_exprs`, but instead of tuples, simple strings pepresenting inames are also allowed. A single @@ -1383,8 +1379,7 @@ def __init__(self, no_sync_with=no_sync_with, within_inames_is_final=within_inames_is_final, within_inames=within_inames, - priority=priority, predicates=predicates, - tags=tags) + priority=priority, predicates=predicates, tags=tags) # {{{ normalize iname_exprs From 0f8594299e24b573c7ec0ef0aa390d7992c9135b Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 16:03:47 -0500 Subject: [PATCH 36/62] update docstring for find_unsatisfied_dependencies and clarify comments in code --- loopy/schedule/checker/__init__.py | 50 ++++++++++++------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index ec4b863fe..0f1f9cd44 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -159,14 +159,14 @@ def find_unsatisfied_dependencies( containing the statements (:class:`loopy.InstructionBase`) whose dependencies will be checked against the linearization items. - :arg lin_items: A list of :class:`loopy.schedule.ScheduleItem` + :arg lin_items: A sequence of :class:`loopy.schedule.ScheduleItem` (to be renamed to `loopy.schedule.LinearizationItem`) containing all linearization items in `knl.linearization`. To allow usage of this routine during linearization, a truncated (i.e. partial) linearization may be passed through this argument. If not provided, `knl.linearization` will be used. - :returns: A list of unsatisfied dependencies, each described using a + :returns: A list of unsatisfied dependencies, each represented as a :class:`collections.namedtuple` containing the following: - `statement_pair`: The (before, after) pair of statement IDs involved @@ -174,21 +174,8 @@ def find_unsatisfied_dependencies( - `dependency`: An class:`islpy.Map` from each instance of the first statement to all instances of the second statement that must occur later. - - `statement_ordering`: A statement ordering information tuple - resulting from `lp.get_pairwise_statement_orderings`, a - :class:`collections.namedtuple` containing the intra-thread - statement instance ordering (SIO) (`sio_intra_thread`), - intra-group SIO (`sio_intra_group`), and global - SIO (`sio_global`), each realized as an :class:`islpy.Map` from each - instance of the first statement to all instances of the second - statement that occur later, as well as the intra-thread pairwise - schedule (`pwsched_intra_thread`), intra-group pairwise schedule - (`pwsched_intra_group`), and the global pairwise schedule - (`pwsched_global`), each containing a pair of mappings from statement - instances to points in a lexicographic ordering, one for each - statement. Note that a pairwise schedule alone cannot be used to - reproduce the corresponding SIO without the corresponding (unique) - lexicographic order map, which is not returned. + - `statement_ordering`: A :class:`StatementOrdering` resulting from + `lp.get_pairwise_statement_orderings` (defined above). """ @@ -206,19 +193,19 @@ def find_unsatisfied_dependencies( # }}} - # {{{ Create map from dependent statement id pairs to dependencies - - # To minimize time complexity, all pairwise schedules will be created - # in one pass, which first requires finding all pairs of statements involved - # in deps. We will also need to collect the deps for each statement pair, - # so do this at the same time. + # {{{ Create map from before->after statement id pairs to dependency maps - stmt_pairs_to_deps = {} + # To minimize time complexity, all pairwise SIOs will be created + # in one pass, which first requires finding all pairs of statements that + # are connected by at least one dependency. + # We will also later need to collect all deps for each statement pair, + # so do this at the same time; create stmt_pairs_to_deps: # stmt_pairs_to_deps: # {(stmt_id_before1, stmt_id_after1): [dep1, dep2, ...], # (stmt_id_before2, stmt_id_after2): [dep1, dep2, ...], # ...} + stmt_pairs_to_deps = {} from loopy.kernel.instruction import BarrierInstruction # TODO (fix) for now, don't check deps on/by barriers @@ -226,13 +213,11 @@ def find_unsatisfied_dependencies( if not isinstance(stmt_after, BarrierInstruction): for before_id, dep_list in stmt_after.dependencies.items(): if not isinstance(knl.id_to_insn[before_id], BarrierInstruction): - # (don't compare dep maps to maps found; - # duplicate deps should be rare) stmt_pairs_to_deps.setdefault( (before_id, stmt_after.id), []).extend(dep_list) # }}} - # {{{ Get statement instance orderings + # {{{ Get statement instance ordering for every before->after statement pair pworders = get_pairwise_statement_orderings( knl, @@ -244,8 +229,9 @@ def find_unsatisfied_dependencies( # {{{ For each depender-dependee pair of statements, check all deps vs. SIO - # Collect info about unsatisfied deps unsatisfied_deps = [] + + # Collect info about unsatisfied deps from collections import namedtuple UnsatisfiedDependencyInfo = namedtuple( "UnsatisfiedDependencyInfo", @@ -267,7 +253,8 @@ def find_unsatisfied_dependencies( aligned_dep_map = ensure_dim_names_match_and_align( dependency, pworder.sio_intra_thread) - # Spaces must match + # {{{ Assert that map spaces match + assert aligned_dep_map.space == pworder.sio_intra_thread.space assert aligned_dep_map.space == pworder.sio_intra_group.space assert aligned_dep_map.space == pworder.sio_global.space @@ -278,6 +265,8 @@ def find_unsatisfied_dependencies( assert (aligned_dep_map.get_var_dict() == pworder.sio_global.get_var_dict()) + # }}} + # Check dependency if not aligned_dep_map.is_subset( pworder.sio_intra_thread | @@ -288,7 +277,8 @@ def find_unsatisfied_dependencies( unsatisfied_deps.append(UnsatisfiedDependencyInfo( stmt_id_pair, aligned_dep_map, pworder)) - # Could break here if we don't care about remaining deps + # Could break here if we only care about correctness and don't + # need to find all unsatisfied deps # }}} From 011ffd55ad94c78615ba9fe6f23c13cfbfa80e69 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 16:05:36 -0500 Subject: [PATCH 37/62] when checking dependencies, don't ignore dependencies on/by barrier statements --- loopy/schedule/checker/__init__.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 0f1f9cd44..fd85361c0 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -207,14 +207,11 @@ def find_unsatisfied_dependencies( # ...} stmt_pairs_to_deps = {} - from loopy.kernel.instruction import BarrierInstruction - # TODO (fix) for now, don't check deps on/by barriers for stmt_after in knl.instructions: - if not isinstance(stmt_after, BarrierInstruction): - for before_id, dep_list in stmt_after.dependencies.items(): - if not isinstance(knl.id_to_insn[before_id], BarrierInstruction): - stmt_pairs_to_deps.setdefault( - (before_id, stmt_after.id), []).extend(dep_list) + for before_id, dep_list in stmt_after.dependencies.items(): + stmt_pairs_to_deps.setdefault( + (before_id, stmt_after.id), []).extend(dep_list) + # }}} # {{{ Get statement instance ordering for every before->after statement pair From 2cddff9e34469ee54ad0317cede2d8b96e45e551 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 17:21:14 -0500 Subject: [PATCH 38/62] document and clean up make_dep_map --- loopy/schedule/checker/utils.py | 79 ++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 70332ef40..a47e12f30 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -139,31 +139,65 @@ def append_mark_to_strings(strings, mark): # {{{ make_dep_map def make_dep_map(s, self_dep=False, knl_with_domains=None): + """Given a string representation of a before->after mapping of statement + instances, create an :class:`islpy.Map` representing the dependency. Insert + a dimension into this map to represent the statement identifier for both + the 'before' and 'after' statements. If ``knl_with_domains`` is provided, + intersect the input and output map domains with the domains for the + inames found in the kernel. + + :arg s: An :class:`str` describing a before->after mapping of statement + instances using islpy map syntax. The input and output spaces + of the map represented by this string should *not* include a dimension + for statement identifiers; these dimension will be added. Inames in + the input space should be suffixed with + ``loopy.schedule.checker.schedule.BEFORE_MARK``. + + :arg self_dep: A :class`bool` expressing whether the depender and + dependee are the same instruction. If so, the value for *both* the + input and output statement identifier dimensions will be set to 0. + If not, the value for the *output* statement identifier dimension will + be set to 1. + + :arg knl_with_domains: A :class:`loopy.kernel.LoopKernel` containing iname + domains that will be used to constrain the inames in the dependency map. + If provided, the domains for the inames found in the dependency will be + intersected with their domains expressed in the kernel. + + :returns: An :class:`islpy.Map` representing a dependency as a mapping from + from each instance of the first statement to all instances of the + second statement that must occur later. - # TODO put this function in the right place + """ from loopy.schedule.checker.schedule import ( BEFORE_MARK, STATEMENT_VAR_NAME, ) + # Pass the input string to isl.Map to initialize the map map_init = isl.Map(s) - # TODO something smarter than this assert - for dim_name in map_init.get_var_names(dt.in_): - assert BEFORE_MARK not in dim_name + # {{{ Islpy drops apostrophes, make sure this hasn't changed + # and manually add the mark if necessary + + if BEFORE_MARK == "'": + for dim_name in map_init.get_var_names(dt.in_): + assert BEFORE_MARK not in dim_name + + # Append BEFORE_MARK to in_ dims + map_marked = append_mark_to_isl_map_var_names( + map_init, dt.in_, BEFORE_MARK) + + # }}} - # append BEFORE_MARK to in-vars - map_marked = append_mark_to_isl_map_var_names( - map_init, dt.in_, BEFORE_MARK) + # {{{ Insert input/output statement dims and set them to 0 or 1 - # insert statement dims: map_with_stmts = insert_and_name_isl_dims( map_marked, dt.in_, [STATEMENT_VAR_NAME+BEFORE_MARK], 0) map_with_stmts = insert_and_name_isl_dims( map_with_stmts, dt.out, [STATEMENT_VAR_NAME], 0) - # assign values 0 or 1 to statement dims sid_after = 0 if self_dep else 1 map_with_stmts = map_with_stmts.add_constraint( @@ -176,21 +210,37 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): map_with_stmts.space, {1: sid_after, STATEMENT_VAR_NAME: -1})) + # }}} + + # {{{ Intersect map domain and range with iname domains in knl + if knl_with_domains is not None: - # intersect map with knl domains + + if BEFORE_MARK != "'": + raise NotImplementedError( + "make_dep_map() does not yet handle a knl_with_domains argument " + "when BEFORE_MARK != \"'\"") + + # {{{ Get inames domain for input and output inames + + # Get the inames from map_init; islpy already dropped the apostrophes inames_in = map_init.get_var_names(dt.in_) inames_out = map_init.get_var_names(dt.out) + # Get inames domain inames_in_dom = knl_with_domains.get_inames_domain( inames_in).project_out_except(inames_in, [dt.set]) inames_out_dom = knl_with_domains.get_inames_domain( inames_out).project_out_except(inames_out, [dt.set]) - # mark dependee inames + # Mark dependee inames inames_in_dom_marked = append_mark_to_isl_map_var_names( inames_in_dom, dt.set, BEFORE_MARK) - # align spaces adds the stmt var + # }}} + + # {{{ Align spaces for iname domains with dep map (which adds the stmt var) + inames_in_dom_marked_aligned = isl.align_spaces( inames_in_dom_marked, map_with_stmts.domain(), obj_bigger_ok=True) # e.g., params might exist @@ -198,10 +248,15 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): inames_out_dom, map_with_stmts.range(), obj_bigger_ok=True) # e.g., params might exist + # }}} + + # Intersect iname domains with dependency map map_with_stmts = map_with_stmts.intersect_range( inames_out_dom_aligned ).intersect_domain(inames_in_dom_marked_aligned) + # }}} + return map_with_stmts # }}} From 516a7d0011001e205e7fa9be59acf102e973426b Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 17:31:03 -0500 Subject: [PATCH 39/62] clean up add_dependency --- loopy/transform/instruction.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 5b5934da4..570ea63cd 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -149,7 +149,7 @@ def add_dep(insn): @for_each_kernel def add_dependency_v2( kernel, stmt_id, depends_on_id, new_dependency): - """Add the statement instance dependency `new_dependency` to the statement with + """Add the statement instance dependency *new_dependency* to the statement with id `stmt_id`. :arg kernel: A :class:`loopy.kernel.LoopKernel`. @@ -166,31 +166,31 @@ def add_dependency_v2( later. """ - # TODO make this accept multiple deps and/or multiple stmts so that - # these can be added in fewer passes through the instructions + + # FIXME Make this allow multiple deps and/or multiple stmts so that + # these can all be in one pass through the instructions if stmt_id not in kernel.id_to_insn: - raise LoopyError("no instructions found matching '%s'," + raise LoopyError("No instructions found matching '%s'," "cannot add dependency %s->%s" % (stmt_id, depends_on_id, stmt_id)) if depends_on_id not in kernel.id_to_insn: - raise LoopyError("no instructions found matching '%s'," + raise LoopyError("No instructions found matching '%s'," "cannot add dependency %s->%s" % (depends_on_id, depends_on_id, stmt_id)) matched = [False] def _add_dep(stmt): - new_deps_dict = stmt.dependencies # dict mapping depends-on ids to dep maps + new_deps_dict = stmt.dependencies # Dict mapping depends-on ids to dep maps matched[0] = True new_deps_dict.setdefault(depends_on_id, []).append(new_dependency) return stmt.copy(dependencies=new_deps_dict) result = map_instructions(kernel, "id:%s" % (stmt_id), _add_dep) - if not matched[0]: # Is this possible, given check above? - raise LoopyError("no instructions found matching '%s' " - "(to which dependencies would be added)" % stmt_id) + # Should have matched (id check above passed) + assert matched[0] return result From 77e031c40af7dfeea67d0a095af81b9b3b2703a6 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 17:50:43 -0500 Subject: [PATCH 40/62] one more test for make_dep_map; clean up dep checking tests --- test/test_linearization_checker.py | 73 ++++++++++++++++-------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index bf2b8e94b..0fb42b398 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1621,9 +1621,10 @@ def test_add_dependency_v2(): dep_b_on_a = make_dep_map( "[pi] -> {{ [i'] -> [i] : i > i' " "and {0} }}".format(assumptions_str), - self_dep=False, knl_with_domains=knl["loopy_kernel"]) + knl_with_domains=knl["loopy_kernel"]) + + # {{{ Test make_dep_map while we're here - # test make_dep_map while we're here: dep_b_on_a_test = _isl_map_with_marked_dims( "[pi] -> {{ [{3}'=0, i'] -> [{3}=1, i] : i > i' " "and {0} and {1} and {2} }}".format( @@ -1634,6 +1635,8 @@ def test_add_dependency_v2(): )) _align_and_compare_maps([(dep_b_on_a, dep_b_on_a_test)]) + # }}} + knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) for stmt in knl["loopy_kernel"].instructions: @@ -1648,9 +1651,10 @@ def test_add_dependency_v2(): dep_b_on_a_2 = make_dep_map( "[pi] -> {{ [i'] -> [i] : i = i' " "and {0}}}".format(assumptions_str), - self_dep=False, knl_with_domains=knl["loopy_kernel"]) + knl_with_domains=knl["loopy_kernel"]) + + # {{{ Test make_dep_map while we're here - # test make_dep_map while we're here: dep_b_on_a_2_test = _isl_map_with_marked_dims( "[pi] -> {{ [{3}'=0, i'] -> [{3}=1, i] : i = i' " "and {0} and {1} and {2} }}".format( @@ -1661,6 +1665,8 @@ def test_add_dependency_v2(): )) _align_and_compare_maps([(dep_b_on_a_2, dep_b_on_a_2_test)]) + # }}} + knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) for stmt in knl["loopy_kernel"].instructions: @@ -1672,24 +1678,16 @@ def test_add_dependency_v2(): assert not stmt.dependencies # Add dependencies to stmt_c - # TODO use make_dep_map instead of _isl_map_with_marked_dims where possible - dep_c_on_a = _isl_map_with_marked_dims( - "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i >= i' " - "and {1} and {2} and {3} }}".format( - STATEMENT_VAR_NAME, - i_range_str, - i_range_str_p, - assumptions_str, - )) - dep_c_on_b = _isl_map_with_marked_dims( - "[pi] -> {{ [{0}'=0, i'] -> [{0}=1, i] : i >= i' " - "and {1} and {2} and {3} }}".format( - STATEMENT_VAR_NAME, - i_range_str, - i_range_str_p, - assumptions_str, - )) + dep_c_on_a = make_dep_map( + "[pi] -> {{ [i'] -> [i] : i >= i' " + "and {0} }}".format(assumptions_str), + knl_with_domains=knl["loopy_kernel"]) + + dep_c_on_b = make_dep_map( + "[pi] -> {{ [i'] -> [i] : i >= i' " + "and {0} }}".format(assumptions_str), + knl_with_domains=knl["loopy_kernel"]) knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_a", dep_c_on_a) knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_b", dep_c_on_b) @@ -1736,7 +1734,6 @@ def test_make_dep_map(): j_range_str = "0 <= j < n" j_range_str_p = "0 <= j' < n" k_range_str = "0 <= k < n" - # k_range_str_p = "0 <= k' < n" # (not used) knl = lp.make_kernel( "{[i,j,k]: %s}" % (" and ".join([i_range_str, j_range_str, k_range_str])), """ @@ -1753,7 +1750,7 @@ def test_make_dep_map(): # Add a dependency to stmt_b dep_b_on_a = make_dep_map( "[n] -> { [i',j'] -> [i,k] : i > i' and j' < k}", - self_dep=False, knl_with_domains=knl["loopy_kernel"]) + knl_with_domains=knl["loopy_kernel"]) # Create expected dep dep_b_on_a_test = _isl_map_with_marked_dims( @@ -1788,6 +1785,14 @@ def test_new_dependencies_finite_diff(): xt_range_str = "0 <= x < nx and 0 <= t < nt" xt_range_str_p = "0 <= x' < nx and 0 <= t' < nt" dep = make_dep_map( + "[nx,nt] -> { [x', t'] -> [x, t] : " + "((x = x' and t = t'+2) or " + " (x'-1 <= x <= x'+1 and t = t' + 1)) }", + self_dep=True, knl_with_domains=knl["loopy_kernel"]) + + # {{{ Test make_dep_map while we're here + + dep_test = make_dep_map( "[nx,nt] -> {{ [x', t'] -> [x, t] : " "((x = x' and t = t'+2) or " " (x'-1 <= x <= x'+1 and t = t' + 1)) and " @@ -1797,11 +1802,15 @@ def test_new_dependencies_finite_diff(): ), self_dep=True) + _align_and_compare_maps([(dep, dep_test)]) + + # }}} + knl = lp.add_dependency_v2(knl, "stmt", "stmt", dep) ref_knl = knl - # {{{ Check with corrct loop nest order + # {{{ Test find_unsatisfied_dependencies with corrct loop nest order # Prioritize loops correctly knl = lp.prioritize_loops(knl, "t,x") @@ -1820,7 +1829,8 @@ def test_new_dependencies_finite_diff(): assert not unsatisfied_deps # }}} - # {{{ Check with incorrect loop nest order + + # {{{ Test find_unsatisfied_dependencies with incorrect loop nest order # Now prioritize loops incorrectly knl = ref_knl @@ -1835,7 +1845,8 @@ def test_new_dependencies_finite_diff(): assert len(unsatisfied_deps) == 1 # }}} - # {{{ Check with parallel x and no barrier + + # {{{ Test find_unsatisfied_dependencies with parallel x and no barrier # Parallelize the x loop knl = ref_knl @@ -1845,7 +1856,7 @@ def test_new_dependencies_finite_diff(): # Make sure unsatisfied deps are caught lin_items, proc_knl, lin_knl = _process_and_linearize(knl) - # Without a barrier, deps not satisfied + # Without a barrier, deps are not satisfied # Make sure there is no barrier, and that unsatisfied deps are caught from loopy.schedule import Barrier for lin_item in lin_items: @@ -1857,7 +1868,8 @@ def test_new_dependencies_finite_diff(): assert len(unsatisfied_deps) == 1 # }}} - # {{{ Check with parallel x and included barrier + + # {{{ Test find_unsatisfied_dependencies with parallel x and included barrier # Insert a barrier to satisfy deps knl = lp.make_kernel( @@ -1882,11 +1894,6 @@ def test_new_dependencies_finite_diff(): # }}} - # Transformations to test after dep handling during transformation: - # knl = lp.split_iname(knl, "x", 14) - # knl = lp.assume(knl, "nx % 14 = 0 and nt >= 1 and nx >= 1") - # knl = lp.tag_inames(knl, "x_outer:g.0, x_inner:l.0") - # }}} # }}} From 63a4c39e50ed1c5398eed168f0b70d035ea071c7 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Mon, 2 Aug 2021 17:59:20 -0500 Subject: [PATCH 41/62] add 'returns' to docstring for add_dependency_v2 --- loopy/transform/instruction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 570ea63cd..d7a88b4a9 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -149,7 +149,7 @@ def add_dep(insn): @for_each_kernel def add_dependency_v2( kernel, stmt_id, depends_on_id, new_dependency): - """Add the statement instance dependency *new_dependency* to the statement with + """Add the statement-instance dependency *new_dependency* to the statement with id `stmt_id`. :arg kernel: A :class:`loopy.kernel.LoopKernel`. @@ -165,6 +165,8 @@ def add_dependency_v2( statement to all instances of the second statement that must occur later. + :returns: The updated :class:`loopy.kernel.LoopKernel` with the new dependency. + """ # FIXME Make this allow multiple deps and/or multiple stmts so that From 56d51ba751a6f3d0d73a0a8f1ef614afabb59d0c Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 13 Aug 2021 16:48:21 -0500 Subject: [PATCH 42/62] use dim_type to abbreviate isl.dim_type class and dt to refer to a particular dim type --- loopy/schedule/checker/utils.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 78afec078..0891c223c 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -182,21 +182,21 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): # and manually add the mark if necessary if BEFORE_MARK == "'": - for dim_name in map_init.get_var_names(dt.in_): + for dim_name in map_init.get_var_names(dim_type.in_): assert BEFORE_MARK not in dim_name # Append BEFORE_MARK to in_ dims map_marked = append_mark_to_isl_map_var_names( - map_init, dt.in_, BEFORE_MARK) + map_init, dim_type.in_, BEFORE_MARK) # }}} # {{{ Insert input/output statement dims and set them to 0 or 1 map_with_stmts = insert_and_name_isl_dims( - map_marked, dt.in_, [STATEMENT_VAR_NAME+BEFORE_MARK], 0) + map_marked, dim_type.in_, [STATEMENT_VAR_NAME+BEFORE_MARK], 0) map_with_stmts = insert_and_name_isl_dims( - map_with_stmts, dt.out, [STATEMENT_VAR_NAME], 0) + map_with_stmts, dim_type.out, [STATEMENT_VAR_NAME], 0) sid_after = 0 if self_dep else 1 @@ -224,18 +224,18 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): # {{{ Get inames domain for input and output inames # Get the inames from map_init; islpy already dropped the apostrophes - inames_in = map_init.get_var_names(dt.in_) - inames_out = map_init.get_var_names(dt.out) + inames_in = map_init.get_var_names(dim_type.in_) + inames_out = map_init.get_var_names(dim_type.out) # Get inames domain inames_in_dom = knl_with_domains.get_inames_domain( - inames_in).project_out_except(inames_in, [dt.set]) + inames_in).project_out_except(inames_in, [dim_type.set]) inames_out_dom = knl_with_domains.get_inames_domain( - inames_out).project_out_except(inames_out, [dt.set]) + inames_out).project_out_except(inames_out, [dim_type.set]) # Mark dependee inames inames_in_dom_marked = append_mark_to_isl_map_var_names( - inames_in_dom, dt.set, BEFORE_MARK) + inames_in_dom, dim_type.set, BEFORE_MARK) # }}} From 19e51d947dac7bf43b64ca39eb69d84dca059349 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 15 Sep 2021 11:29:47 -0500 Subject: [PATCH 43/62] improve comment --- loopy/schedule/checker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index cdec371c3..3c8ad06d4 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -197,7 +197,7 @@ def find_unsatisfied_dependencies( # {{{ Create map from before->after statement id pairs to dependency maps - # To minimize time complexity, all pairwise SIOs will be created + # For efficiency, all pairwise SIOs will be created # in one pass, which first requires finding all pairs of statements that # are connected by at least one dependency. # We will also later need to collect all deps for each statement pair, From d3b6079c9327d55b0c369fb3cd8f5ee3ed11abc8 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 15 Sep 2021 19:35:24 -0500 Subject: [PATCH 44/62] eliminate duplicated code in subtraction map creation by adding function _pad_tuples_and_assign_integer_vals_to_map_template --- loopy/schedule/checker/schedule.py | 86 ++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/loopy/schedule/checker/schedule.py b/loopy/schedule/checker/schedule.py index 05886877e..248700209 100644 --- a/loopy/schedule/checker/schedule.py +++ b/loopy/schedule/checker/schedule.py @@ -567,27 +567,55 @@ def _gather_blex_ordering_info( # Create mapping (dict) from iname to corresponding blex dim name # TODO rename to "seq_..." - iname_to_blex_var = {} - iname_to_iname_prime = {} + seq_iname_to_blex_var = {} for iname, dim in iname_to_blex_dim.items(): - iname_to_iname_prime[iname] = iname+BEFORE_MARK - iname_to_blex_var[iname] = seq_blex_dim_names[dim] - iname_to_blex_var[iname+BEFORE_MARK] = seq_blex_dim_names_prime[dim] + seq_iname_to_blex_var[iname] = seq_blex_dim_names[dim] + seq_iname_to_blex_var[iname+BEFORE_MARK] = seq_blex_dim_names_prime[dim] - # Get a map representing blex_order_map space - # (Note that this template cannot be created until *after* the intersection + # {{{ Get a template map matching blex_order_map.space that will serve as + # the starting point when creating the maps to subtract from blex_order_map + + # This template includes concurrent inames as params, both marked + # ('before') and unmarked ('after'). + # Note that this template cannot be created until *after* the intersection # of blex_order_map with all_blex_points above, otherwise the template will - # be missing necessary parameters) + # be missing necessary parameters. blex_map_template = isl.align_spaces( isl.Map("[ ] -> { [ ] -> [ ] }"), blex_order_map) blex_set_template = blex_map_template.range() + # }}} + + # {{{ _pad_tuples_and_assign_integer_vals_to_map_template() helper + + seq_blex_in_out_dim_names = seq_blex_dim_names_prime + seq_blex_dim_names + + def _pad_tuples_and_assign_integer_vals_to_map_template( + in_tuple, out_tuple): + # External variables read (not written): + # n_seq_blex_dims, seq_blex_in_out_dim_names, blex_map_template + + # Pad the tuples + in_tuple_padded = _pad_tuple_with_zeros(in_tuple, n_seq_blex_dims) + out_tuple_padded = _pad_tuple_with_zeros(out_tuple, n_seq_blex_dims) + + # Assign map values for ints only + map_with_int_vals_assigned = blex_map_template + for dim_name, val in zip( + seq_blex_in_out_dim_names, + in_tuple_padded+out_tuple_padded): + if isinstance(val, int): + map_with_int_vals_assigned = add_eq_isl_constraint_from_names( + map_with_int_vals_assigned, dim_name, val) + + return map_with_int_vals_assigned + + # }}} + # {{{ Create blex map to subtract for each iname in blex_exclusion_info maps_to_subtract = [] for iname, key_lex_tuples in blex_exclusion_info.items(): - print("") - print(iname) # {{{ Create blex map to subtract for one iname @@ -621,6 +649,7 @@ def _gather_blex_ordering_info( # PRE dim vals should all be inames (bounded later) or ints (assign now). # FIRST dim values will be inames, ints, or one of our lexmin bounds. + # TODO remove after sanity tests: # Pad PRE tuple pre_tuple_padded = _pad_tuple_with_zeros( key_lex_tuples[slex.PRE], n_seq_blex_dims) @@ -629,12 +658,19 @@ def _gather_blex_ordering_info( first_tuple_padded = _pad_tuple_with_zeros(first_tuple, n_seq_blex_dims) # Create PRE->FIRST map and assign int (non-iname) dim values. - pre_to_first_map = _add_eq_isl_constraints_for_ints_only( + _pre_to_first_map = _add_eq_isl_constraints_for_ints_only( blex_map_template, zip( seq_blex_dim_names_prime+seq_blex_dim_names, pre_tuple_padded+first_tuple_padded)) + pre_to_first_map = _pad_tuples_and_assign_integer_vals_to_map_template( + key_lex_tuples[slex.PRE], first_tuple) + + # TODO remove: + assert _pre_to_first_map == pre_to_first_map + assert _pre_to_first_map.get_var_dict() == pre_to_first_map.get_var_dict() + # Get the set representing the value of the iname on the first # iteration of the loop loop_min_bound = loop_bounds[iname][0] @@ -645,7 +681,7 @@ def _gather_blex_ordering_info( # spaces loop_min_bound = find_and_rename_dims( loop_min_bound, dim_type.set, - {k: iname_to_blex_var[k] for k in first_tuple[1::2]}) + {k: seq_iname_to_blex_var[k] for k in first_tuple[1::2]}) # Align with blex space (adds needed dims) loop_first_set = isl.align_spaces(loop_min_bound, blex_set_template) @@ -664,6 +700,7 @@ def _gather_blex_ordering_info( # BOTTOM/TOP dim vals should all be inames (bounded later) or ints # (assign now). + # TODO remove after sanity tests: # Pad BOTTOM tuple bottom_tuple_padded = _pad_tuple_with_zeros( key_lex_tuples[slex.BOTTOM], n_seq_blex_dims) @@ -672,14 +709,21 @@ def _gather_blex_ordering_info( key_lex_tuples[slex.TOP], n_seq_blex_dims) # Create BOTTOM->TOP map and assign int (non-iname) dim values. - bottom_to_top_map = _add_eq_isl_constraints_for_ints_only( + _bottom_to_top_map = _add_eq_isl_constraints_for_ints_only( blex_map_template, zip( seq_blex_dim_names_prime+seq_blex_dim_names, bottom_tuple_padded+top_tuple_padded)) + bottom_to_top_map = _pad_tuples_and_assign_integer_vals_to_map_template( + key_lex_tuples[slex.BOTTOM], key_lex_tuples[slex.TOP]) + + # TODO remove after sanity tests: + assert _bottom_to_top_map == bottom_to_top_map + assert _bottom_to_top_map.get_var_dict() == bottom_to_top_map.get_var_dict() + # Add constraint iname = iname' + 1 - blex_var_for_iname = iname_to_blex_var[iname] + blex_var_for_iname = seq_iname_to_blex_var[iname] bottom_to_top_map = bottom_to_top_map.add_constraint( isl.Constraint.eq_from_names( bottom_to_top_map.space, @@ -692,6 +736,7 @@ def _gather_blex_ordering_info( # POST dim vals should all be inames (bounded later) or ints (assign now). # LAST dim values will be inames, ints, or one of our lexmax bounds. + # TODO remove after sanity tests: # Pad POST tuple post_tuple_padded = _pad_tuple_with_zeros( key_lex_tuples[slex.POST], n_seq_blex_dims) @@ -700,12 +745,19 @@ def _gather_blex_ordering_info( last_tuple_padded = _pad_tuple_with_zeros(last_tuple, n_seq_blex_dims) # Create LAST->POST map and assign int (non-iname) dim values. - last_to_post_map = _add_eq_isl_constraints_for_ints_only( + _last_to_post_map = _add_eq_isl_constraints_for_ints_only( blex_map_template, zip( seq_blex_dim_names_prime+seq_blex_dim_names, last_tuple_padded+post_tuple_padded)) + last_to_post_map = _pad_tuples_and_assign_integer_vals_to_map_template( + last_tuple, key_lex_tuples[slex.POST]) + + # TODO remove after sanity tests: + assert _last_to_post_map == last_to_post_map + assert _last_to_post_map.get_var_dict() == last_to_post_map.get_var_dict() + # Get the set representing the value of the iname on the last # iteration of the loop loop_max_bound = loop_bounds[iname][1] @@ -715,7 +767,7 @@ def _gather_blex_ordering_info( # spaces loop_max_bound = find_and_rename_dims( loop_max_bound, dim_type.set, - {k: iname_to_blex_var[k] for k in last_tuple[1::2]}) + {k: seq_iname_to_blex_var[k] for k in last_tuple[1::2]}) # There may be concurrent inames in the dim_type.param dimensions of # the loop_max_bound, and we need to append the BEFORE_MARK to those @@ -746,7 +798,7 @@ def _gather_blex_ordering_info( # Add condition to fix iter value for *surrounding* sequential loops (j = j') # (odd indices in key_lex_tuples[PRE] contain the sounding inames) for seq_surrounding_iname in key_lex_tuples[slex.PRE][1::2]: - s_blex_var = iname_to_blex_var[seq_surrounding_iname] + s_blex_var = seq_iname_to_blex_var[seq_surrounding_iname] map_to_subtract = add_eq_isl_constraint_from_names( map_to_subtract, s_blex_var, s_blex_var+BEFORE_MARK) From 67b52e47292fd5bb22e7476c22339072b49d78c8 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Wed, 15 Sep 2021 21:15:39 -0500 Subject: [PATCH 45/62] fix weird merging issue --- loopy/schedule/checker/schedule.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/loopy/schedule/checker/schedule.py b/loopy/schedule/checker/schedule.py index a0685fdc9..0b2151854 100644 --- a/loopy/schedule/checker/schedule.py +++ b/loopy/schedule/checker/schedule.py @@ -652,9 +652,6 @@ def _pad_tuples_and_assign_integer_vals_to_map_template( pre_to_first_map = _pad_tuples_and_assign_integer_vals_to_map_template( key_lex_tuples[slex.PRE], top_tuple) - pre_to_first_map = _pad_tuples_and_assign_integer_vals_to_map_template( - key_lex_tuples[slex.PRE], first_tuple) - # Get the set representing the value of the iname on the first # iteration of the loop loop_min_bound = loop_bounds[iname][0] @@ -708,9 +705,6 @@ def _pad_tuples_and_assign_integer_vals_to_map_template( last_to_post_map = _pad_tuples_and_assign_integer_vals_to_map_template( bottom_tuple, key_lex_tuples[slex.POST]) - last_to_post_map = _pad_tuples_and_assign_integer_vals_to_map_template( - last_tuple, key_lex_tuples[slex.POST]) - # Get the set representing the value of the iname on the last # iteration of the loop loop_max_bound = loop_bounds[iname][1] From 883df7617e5491360139691bfd8295a76dfcb62d Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 19:23:33 -0500 Subject: [PATCH 46/62] revise docstring for dependencies attribute of instruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- loopy/kernel/instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index ebafb9d5e..378eea075 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -115,7 +115,7 @@ class InstructionBase(ImmutableRecord, Taggable): express dependency relationships by mapping each instance of the dependee statement to all instances of this statement that must occur later. This dict expresses the new statement-instance-level - dependencies and will eventually replace the `depends_on` attribute. + dependencies and will eventually replace :attr:`depends_on`. .. attribute:: depends_on_is_final From 2ba8a2ab1c4885ffc60b5ecaa4997f9a4ceef4c5 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 19:23:53 -0500 Subject: [PATCH 47/62] revise docstring for dependencies attribute of instruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- loopy/kernel/instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 378eea075..ac3244df0 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -108,7 +108,7 @@ class InstructionBase(ImmutableRecord, Taggable): .. attribute:: dependencies - A :class:`dict` mapping :attr:`id` values of :class:`InstructionBase` + A :class:`dict` mapping :attr:`id` values instances, each referring to a dependee statement (i.e., a statement with statement instances that must be executed before instances of this statement), to lists (one list per key) of class:`islpy.Map`\ s that From fef6322d5e0ea80b486c54a7a3368bc7568c9c4b Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 19:25:30 -0500 Subject: [PATCH 48/62] handle dependencies in instruction.__setstate__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andreas Klöckner --- loopy/kernel/instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index ac3244df0..38fda0577 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -479,7 +479,7 @@ def __setstate__(self, val): if self.id is not None: # pylint:disable=access-member-before-definition self.id = intern(self.id) self.depends_on = intern_frozenset_of_ids(self.depends_on) - # FIXME Do something with self.dependencies? + self.dependencies = {intern(id): deps for id, deps in self.dependencies.items()} self.groups = intern_frozenset_of_ids(self.groups) self.conflicts_with_groups = ( intern_frozenset_of_ids(self.conflicts_with_groups)) From cb52c107062d15991422beaf366041d33302c43e Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 20:47:24 -0500 Subject: [PATCH 49/62] combine add_dependency_v2 with add_dependency by accepting a two-tuple as the third argument; if the third argument is not a two-tuple, add legacy deps as before, else add contemporary dep; this change also enables contemporary deps to be added to multiple statements in one pass --- loopy/transform/instruction.py | 118 +++++++++++------------------ test/test_linearization_checker.py | 14 ++-- 2 files changed, 53 insertions(+), 79 deletions(-) diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index d7a88b4a9..6c05b45d0 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -98,11 +98,24 @@ def set_prio(insn): @for_each_kernel def add_dependency(kernel, insn_match, depends_on): - """Add the instruction dependency *dependency* to the instructions matched + """Add dependency contained in *depends_on* to the instructions matched by *insn_match*. - *insn_match* and *depends_on* may be any instruction id match understood by - :func:`loopy.match.parse_match`. + :arg kernel: A :class:`loopy.kernel.LoopKernel`. + + :arg insn_match: An instruction id match understood by + :func:`loopy.match.parse_match` identifying the statement to which the + dependency will be added. + + :arg depends_on: A two-tuple containing an instruction id match understood by + :func:`loopy.match.parse_match` identifying the dependee statement(s), + and an class:`islpy.Map` from each instance of the dependee + statement(s) to all instances of the depender statement(s) that must occur + later. For backward compatability, *depends_on* may also be an + instruction id match identifying dependee statement ids to be added to + `stmt.depends_on` for any stmt matching *insn_match*. + + :returns: The updated :class:`loopy.kernel.LoopKernel` with the new dependency. .. versionchanged:: 2016.3 @@ -110,28 +123,44 @@ def add_dependency(kernel, insn_match, depends_on): be not just ID but also match expression. """ + # Determine whether we received legacy or contemporary dependency + if isinstance(depends_on, tuple): + dep_map = depends_on[1] + depends_on = depends_on[0] + else: + dep_map = None + if isinstance(depends_on, str) and depends_on in kernel.id_to_insn: - added_deps = frozenset([depends_on]) + dependee_ids = frozenset([depends_on]) else: - added_deps = frozenset( - dep.id for dep in find_instructions_in_single_kernel(kernel, - depends_on)) + dependee_ids = frozenset( + dependee.id for dependee in find_instructions_in_single_kernel( + kernel, depends_on)) - if not added_deps: + if not dependee_ids: raise LoopyError("no instructions found matching '%s' " "(to add as dependencies)" % depends_on) matched = [False] - def add_dep(insn): - new_deps = insn.depends_on - matched[0] = True - if new_deps is None: - new_deps = added_deps - else: - new_deps = new_deps | added_deps - - return insn.copy(depends_on=new_deps) + if dep_map is None: + # Handle legacy dependencies + def add_dep(insn): + new_deps = insn.depends_on # Set of dependee ids + matched[0] = True + if new_deps is None: + new_deps = dependee_ids + else: + new_deps = new_deps | dependee_ids + return insn.copy(depends_on=new_deps) + else: + # Handle contemporary dependencies + def add_dep(stmt): + new_deps_dict = stmt.dependencies # Mapping of dependee ids to dep maps + matched[0] = True + for dependee_id in dependee_ids: + new_deps_dict.setdefault(dependee_id, []).append(dep_map) + return stmt.copy(dependencies=new_deps_dict) result = map_instructions(kernel, insn_match, add_dep) @@ -144,61 +173,6 @@ def add_dep(insn): # }}} -# {{{ add_dependency_v2 - -@for_each_kernel -def add_dependency_v2( - kernel, stmt_id, depends_on_id, new_dependency): - """Add the statement-instance dependency *new_dependency* to the statement with - id `stmt_id`. - - :arg kernel: A :class:`loopy.kernel.LoopKernel`. - - :arg stmt_id: The :class:`str` statement identifier of the statement to - which the dependency will be added. - - :arg depends_on_id: The :class:`str` identifier of the statement that is - depended on, i.e., the statement with statement instances that must - happen before those of `stmt_id`. - - :arg new_dependency: An class:`islpy.Map` from each instance of the first - statement to all instances of the second statement that must occur - later. - - :returns: The updated :class:`loopy.kernel.LoopKernel` with the new dependency. - - """ - - # FIXME Make this allow multiple deps and/or multiple stmts so that - # these can all be in one pass through the instructions - - if stmt_id not in kernel.id_to_insn: - raise LoopyError("No instructions found matching '%s'," - "cannot add dependency %s->%s" - % (stmt_id, depends_on_id, stmt_id)) - if depends_on_id not in kernel.id_to_insn: - raise LoopyError("No instructions found matching '%s'," - "cannot add dependency %s->%s" - % (depends_on_id, depends_on_id, stmt_id)) - - matched = [False] - - def _add_dep(stmt): - new_deps_dict = stmt.dependencies # Dict mapping depends-on ids to dep maps - matched[0] = True - new_deps_dict.setdefault(depends_on_id, []).append(new_dependency) - return stmt.copy(dependencies=new_deps_dict) - - result = map_instructions(kernel, "id:%s" % (stmt_id), _add_dep) - - # Should have matched (id check above passed) - assert matched[0] - - return result - -# }}} - - # {{{ remove_instructions def _toposort_of_subset_of_insns(kernel, subset_insns): diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index 887617140..cf4b427ec 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1778,9 +1778,9 @@ def test_blex_map_transitivity_with_duplicate_conc_inames(): # {{{ Dependency tests -# {{{ test_add_dependency_v2 +# {{{ test_add_dependency_with_new_deps -def test_add_dependency_v2(): +def test_add_dependency_with_new_deps(): # Make kernel and use OLD deps to control linearization order for now i_range_str = "0 <= i < pi" @@ -1821,7 +1821,7 @@ def test_add_dependency_v2(): # }}} - knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a) + knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a)) for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": @@ -1851,7 +1851,7 @@ def test_add_dependency_v2(): # }}} - knl = lp.add_dependency_v2(knl, "stmt_b", "stmt_a", dep_b_on_a_2) + knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a_2)) for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": @@ -1873,8 +1873,8 @@ def test_add_dependency_v2(): "and {0} }}".format(assumptions_str), knl_with_domains=knl["loopy_kernel"]) - knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_a", dep_c_on_a) - knl = lp.add_dependency_v2(knl, "stmt_c", "stmt_b", dep_c_on_b) + knl = lp.add_dependency(knl, "id:stmt_c", ("id:stmt_a", dep_c_on_a)) + knl = lp.add_dependency(knl, "id:stmt_c", ("id:stmt_b", dep_c_on_b)) for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": @@ -1990,7 +1990,7 @@ def test_new_dependencies_finite_diff(): # }}} - knl = lp.add_dependency_v2(knl, "stmt", "stmt", dep) + knl = lp.add_dependency(knl, "id:stmt", ("id:stmt", dep)) ref_knl = knl From fea47e8e31c2f833cfc470ff92cc58c7aebfe502 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 20:50:31 -0500 Subject: [PATCH 50/62] remove add_dependency_v2 from imports --- loopy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index 8a1e05fd4..00b21e3d5 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -81,7 +81,7 @@ from loopy.transform.instruction import ( find_instructions, map_instructions, set_instruction_priority, - add_dependency, add_dependency_v2, + add_dependency, remove_instructions, replace_instruction_ids, tag_instructions, @@ -216,7 +216,7 @@ "find_instructions", "map_instructions", "set_instruction_priority", - "add_dependency", "add_dependency_v2", + "add_dependency", "remove_instructions", "replace_instruction_ids", "tag_instructions", From b05d5deffd2a74d05335946375397d1736de75d7 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 20:54:16 -0500 Subject: [PATCH 51/62] placate flake8 --- loopy/kernel/instruction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 38fda0577..0cf0e4c4c 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -479,7 +479,9 @@ def __setstate__(self, val): if self.id is not None: # pylint:disable=access-member-before-definition self.id = intern(self.id) self.depends_on = intern_frozenset_of_ids(self.depends_on) - self.dependencies = {intern(id): deps for id, deps in self.dependencies.items()} + self.dependencies = { + intern(dependee_id): deps + for dependee_id, deps in self.dependencies.items()} self.groups = intern_frozenset_of_ids(self.groups) self.conflicts_with_groups = ( intern_frozenset_of_ids(self.conflicts_with_groups)) From a44e4b68d28ba6a35d90ada9f35bac7b341b886a Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 21:20:08 -0500 Subject: [PATCH 52/62] more details in docstring for stmt.dependencies attribute --- loopy/kernel/instruction.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 0cf0e4c4c..60cc62dd5 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -114,8 +114,21 @@ class InstructionBase(ImmutableRecord, Taggable): statement), to lists (one list per key) of class:`islpy.Map`\ s that express dependency relationships by mapping each instance of the dependee statement to all instances of this statement that must occur - later. This dict expresses the new statement-instance-level - dependencies and will eventually replace :attr:`depends_on`. + later. + + The `in_` space of a dependency map must contain one dimension per + iname in :attr:`within_inames` for the dependee, and the `out` space + must contain one dimension per iname in :attr:`within_inames` for this + statement. The dimension names should match the corresponding iname, + with those in the `in_` space suffixed by + :data:`loopy.schedule.checker.schedule.BEFORE_MARK`. Reduction inames + are not considered (for now). Only dependencies involving instances of + statements within the domain on either end of the map are expected to + be represented. Creation of these maps may be facilitated + with :func:`loopy.schedule.checker.utils.make_dep_map`. + + This dict expresses the new statement-instance-level dependencies and + will eventually replace :attr:`depends_on`. .. attribute:: depends_on_is_final From c0890f59f8899e2a14e95f70f6cad23d7c6f9a9f Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 21:28:04 -0500 Subject: [PATCH 53/62] add stop_on_first_violation arg to find_unsatisfied_dependencies() --- loopy/schedule/checker/__init__.py | 9 +++++++-- test/test_linearization_checker.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 619deb767..28677b2ed 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -156,6 +156,7 @@ def get_pairwise_statement_orderings( def find_unsatisfied_dependencies( knl, lin_items=None, + stop_on_first_violation=True, ): """For each statement (:class:`loopy.InstructionBase`) found in a preprocessed kernel, determine which dependencies, if any, have been @@ -173,6 +174,9 @@ def find_unsatisfied_dependencies( linearization may be passed through this argument. If not provided, `knl.linearization` will be used. + :arg stop_on_first_violation: A :class:`bool` determining whether to stop + checking dependencies once the first unsatisfied dependency is found. + :returns: A list of unsatisfied dependencies, each represented as a :class:`collections.namedtuple` containing the following: @@ -281,8 +285,9 @@ def find_unsatisfied_dependencies( unsatisfied_deps.append(UnsatisfiedDependencyInfo( stmt_id_pair, aligned_dep_map, pworder)) - # Could break here if we only care about correctness and don't - # need to find all unsatisfied deps + # Break here if stop_on_first_violation==True + if stop_on_first_violation: + break # }}} diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index cf4b427ec..a2f6deec7 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -2024,7 +2024,7 @@ def test_new_dependencies_finite_diff(): lin_items, proc_knl, lin_knl = _process_and_linearize(knl) unsatisfied_deps = lp.find_unsatisfied_dependencies( - proc_knl, lin_items) + proc_knl, lin_items, stop_on_first_violation=False) assert len(unsatisfied_deps) == 1 @@ -2047,7 +2047,7 @@ def test_new_dependencies_finite_diff(): assert not isinstance(lin_item, Barrier) unsatisfied_deps = lp.find_unsatisfied_dependencies( - proc_knl, lin_items) + proc_knl, lin_items, stop_on_first_violation=False) assert len(unsatisfied_deps) == 1 From 73f832ca94e2f258125831d5f46ed11ed3e2fa12 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 21:39:06 -0500 Subject: [PATCH 54/62] add FIXME about potentially checking individual parts of deps/sios --- loopy/schedule/checker/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 28677b2ed..61565cf38 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -276,11 +276,16 @@ def find_unsatisfied_dependencies( # }}} # Check dependency - if not aligned_dep_map.is_subset( + if not aligned_dep_map <= ( pworder.sio_intra_thread | pworder.sio_intra_group | pworder.sio_global ): + # FIXME This could be done by computing (via intersection) + # intra-thread, intra-group, and global parts of aligned_dep_map + # and demanding that each is a subset of the corresponding sio. + # Determine whether this would be more efficient, and if so, do + # it. unsatisfied_deps.append(UnsatisfiedDependencyInfo( stmt_id_pair, aligned_dep_map, pworder)) From 539c39c8a963ec10130cc6e3f869fd552bf5ab26 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Thu, 16 Sep 2021 21:59:06 -0500 Subject: [PATCH 55/62] add helpful comments/docstrings to dependency checking test functions --- test/test_linearization_checker.py | 82 +++++++++++++++++------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/test/test_linearization_checker.py b/test/test_linearization_checker.py index a2f6deec7..4258a2c52 100644 --- a/test/test_linearization_checker.py +++ b/test/test_linearization_checker.py @@ -1781,6 +1781,10 @@ def test_blex_map_transitivity_with_duplicate_conc_inames(): # {{{ test_add_dependency_with_new_deps def test_add_dependency_with_new_deps(): + """Use add_dependency to add new deps to kernels and make sure that the + correct dep is being added to the correct instruction. Also make sure that + these deps can be succesfully checked for violation. Also, while we're + here, test to make sure make_dep_map() produces the correct result.""" # Make kernel and use OLD deps to control linearization order for now i_range_str = "0 <= i < pi" @@ -1804,8 +1808,19 @@ def test_add_dependency_with_new_deps(): # Add a dependency to stmt_b dep_b_on_a = make_dep_map( "[pi] -> {{ [i'] -> [i] : i > i' " - "and {0} }}".format(assumptions_str), + "and {0} " + "}}".format(assumptions_str), knl_with_domains=knl["loopy_kernel"]) + knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a)) + + # Make sure knl instructions all have the expected deps + for stmt in knl["loopy_kernel"].instructions: + if stmt.id == "stmt_b": + assert stmt.dependencies == { + "stmt_a": [dep_b_on_a, ], + } + else: + assert not stmt.dependencies # {{{ Test make_dep_map while we're here @@ -1821,22 +1836,23 @@ def test_add_dependency_with_new_deps(): # }}} - knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a)) + # Add a second dependency to stmt_b + dep_b_on_a_2 = make_dep_map( + "[pi] -> {{ [i'] -> [i] : i = i' " + "and {0}" + "}}".format(assumptions_str), + knl_with_domains=knl["loopy_kernel"]) + knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a_2)) + # Make sure knl instructions all have the expected deps for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": assert stmt.dependencies == { - "stmt_a": [dep_b_on_a, ], + "stmt_a": [dep_b_on_a, dep_b_on_a_2], } else: assert not stmt.dependencies - # Add a second dependency to stmt_b - dep_b_on_a_2 = make_dep_map( - "[pi] -> {{ [i'] -> [i] : i = i' " - "and {0}}}".format(assumptions_str), - knl_with_domains=knl["loopy_kernel"]) - # {{{ Test make_dep_map while we're here dep_b_on_a_2_test = _isl_map_with_marked_dims( @@ -1851,31 +1867,24 @@ def test_add_dependency_with_new_deps(): # }}} - knl = lp.add_dependency(knl, "id:stmt_b", ("id:stmt_a", dep_b_on_a_2)) - - for stmt in knl["loopy_kernel"].instructions: - if stmt.id == "stmt_b": - assert stmt.dependencies == { - "stmt_a": [dep_b_on_a, dep_b_on_a_2], - } - else: - assert not stmt.dependencies - # Add dependencies to stmt_c dep_c_on_a = make_dep_map( "[pi] -> {{ [i'] -> [i] : i >= i' " - "and {0} }}".format(assumptions_str), + "and {0} " + "}}".format(assumptions_str), knl_with_domains=knl["loopy_kernel"]) dep_c_on_b = make_dep_map( "[pi] -> {{ [i'] -> [i] : i >= i' " - "and {0} }}".format(assumptions_str), + "and {0} " + "}}".format(assumptions_str), knl_with_domains=knl["loopy_kernel"]) knl = lp.add_dependency(knl, "id:stmt_c", ("id:stmt_a", dep_c_on_a)) knl = lp.add_dependency(knl, "id:stmt_c", ("id:stmt_b", dep_c_on_b)) + # Make sure knl instructions all have the expected deps for stmt in knl["loopy_kernel"].instructions: if stmt.id == "stmt_b": assert stmt.dependencies == { @@ -1889,7 +1898,8 @@ def test_add_dependency_with_new_deps(): else: assert not stmt.dependencies - # Now make sure deps are satisfied + # {{{ Now make sure deps can be checked. These should be satisfied. + lin_items, proc_knl, lin_knl = _process_and_linearize(knl) unsatisfied_deps = lp.find_unsatisfied_dependencies( @@ -1897,20 +1907,23 @@ def test_add_dependency_with_new_deps(): assert not unsatisfied_deps - # Make sure dep checking also works with just linearized kernel + # Make sure dep checking also works when only the linearized kernel is + # provided to find_unsatisfied_dependencies() unsatisfied_deps = lp.find_unsatisfied_dependencies(lin_knl) assert not unsatisfied_deps + # }}} + # }}} # {{{ test_make_dep_map def test_make_dep_map(): - # This is also tested inside other test functions, but - # here we specifically test case where the statement inames - # don't match + """Make sure make_dep_map() produces the desired result. This is also + tested inside other test functions, but here we specifically test cases + where the statement inames don't match.""" # Make kernel and use OLD deps to control linearization order for now i_range_str = "0 <= i < n" @@ -1956,6 +1969,8 @@ def test_make_dep_map(): # {{{ test_new_dependencies_finite_diff: def test_new_dependencies_finite_diff(): + """Test find_unsatisfied_dependencies() using several variants of a finite + difference kernel, some of which violate dependencies.""" # Define kernel knl = lp.make_kernel( @@ -1965,33 +1980,28 @@ def test_new_dependencies_finite_diff(): knl = lp.add_dtypes( knl, {"u": np.float32, "dx": np.float32, "dt": np.float32}) - # Define dependency - xt_range_str = "0 <= x < nx and 0 <= t < nt" - xt_range_str_p = "0 <= x' < nx and 0 <= t' < nt" + # Define and add dependency dep = make_dep_map( "[nx,nt] -> { [x', t'] -> [x, t] : " "((x = x' and t = t'+2) or " " (x'-1 <= x <= x'+1 and t = t' + 1)) }", self_dep=True, knl_with_domains=knl["loopy_kernel"]) + knl = lp.add_dependency(knl, "id:stmt", ("id:stmt", dep)) # {{{ Test make_dep_map while we're here dep_test = make_dep_map( - "[nx,nt] -> {{ [x', t'] -> [x, t] : " + "[nx,nt] -> { [x', t'] -> [x, t] : " "((x = x' and t = t'+2) or " " (x'-1 <= x <= x'+1 and t = t' + 1)) and " - "{0} and {1} }}".format( - xt_range_str, - xt_range_str_p, - ), + "0 <= x < nx and 0 <= t < nt and " + "0 <= x' < nx and 0 <= t' < nt }", self_dep=True) _align_and_compare_maps([(dep, dep_test)]) # }}} - knl = lp.add_dependency(knl, "id:stmt", ("id:stmt", dep)) - ref_knl = knl # {{{ Test find_unsatisfied_dependencies with corrct loop nest order From c7b3d1830a263a42e5c167a255725576c19ec548 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 12:36:22 -0500 Subject: [PATCH 56/62] also describe statement dims of instruction.dependencies attribute --- loopy/kernel/instruction.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/loopy/kernel/instruction.py b/loopy/kernel/instruction.py index 60cc62dd5..a29447b6b 100644 --- a/loopy/kernel/instruction.py +++ b/loopy/kernel/instruction.py @@ -116,16 +116,26 @@ class InstructionBase(ImmutableRecord, Taggable): dependee statement to all instances of this statement that must occur later. - The `in_` space of a dependency map must contain one dimension per - iname in :attr:`within_inames` for the dependee, and the `out` space - must contain one dimension per iname in :attr:`within_inames` for this - statement. The dimension names should match the corresponding iname, - with those in the `in_` space suffixed by - :data:`loopy.schedule.checker.schedule.BEFORE_MARK`. Reduction inames - are not considered (for now). Only dependencies involving instances of - statements within the domain on either end of the map are expected to - be represented. Creation of these maps may be facilitated - with :func:`loopy.schedule.checker.utils.make_dep_map`. + The name of the first dimension in the `in_` and `out` spaces must be + :data:`loopy.schedule.checker.schedule.STATEMENT_VAR_NAME`, suffixed by + :data:`loopy.schedule.checker.schedule.BEFORE_MARK` for the `in_` + dimension. This dimension in the `in_` space must be assigned the value + 0, and in the `out` space it must be assigned 0 for self-dependencies + (dependencies describing instances of a statement that must happen + before other instances of the same statement) and 1 otherwise. + + In addition to the statement dimension, the `in_` space of a dependency + map must contain one dimension per iname in :attr:`within_inames` for + the dependee, and the `out` space must contain one dimension per iname + in :attr:`within_inames` for this statement. The dimension names should + match the corresponding iname, with those in the `in_` space suffixed + by :data:`loopy.schedule.checker.schedule.BEFORE_MARK`. Reduction + inames are not considered (for now). Only dependencies involving + instances of statements within the domain on either end of the map are + expected to be represented. + + Creation of these maps may be facilitated with + :func:`loopy.schedule.checker.utils.make_dep_map`. This dict expresses the new statement-instance-level dependencies and will eventually replace :attr:`depends_on`. From b1514031f442043ec174010c0020517a2ffca30b Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 16:03:27 -0500 Subject: [PATCH 57/62] add get_pairwise_statement_orderings to loopy.__init__ --- loopy/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/loopy/__init__.py b/loopy/__init__.py index 00b21e3d5..b11edfa84 100644 --- a/loopy/__init__.py +++ b/loopy/__init__.py @@ -131,7 +131,9 @@ generate_loop_schedules, get_one_scheduled_kernel, get_one_linearized_kernel, linearize) from loopy.schedule.checker import ( - find_unsatisfied_dependencies) + get_pairwise_statement_orderings, + find_unsatisfied_dependencies, + ) from loopy.statistics import (ToCountMap, ToCountPolynomialMap, CountGranularity, stringify_stats_mapping, Op, MemAccess, get_op_map, get_mem_access_map, get_synchronization_map, gather_access_footprints, @@ -272,6 +274,7 @@ "generate_loop_schedules", "get_one_scheduled_kernel", "get_one_linearized_kernel", "linearize", + "get_pairwise_statement_orderings", "find_unsatisfied_dependencies", "GeneratedProgram", "CodeGenerationResult", "PreambleInfo", From d99d8ba0b9e278a81f109a0cb11331f5d325de85 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 16:03:56 -0500 Subject: [PATCH 58/62] add new dep checking stuff to documentation --- doc/ref_other.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/ref_other.rst b/doc/ref_other.rst index b13f39869..9b3547421 100644 --- a/doc/ref_other.rst +++ b/doc/ref_other.rst @@ -26,6 +26,15 @@ Automatic Testing .. autofunction:: auto_test_vs_ref +Checking Dependencies at the Statement-Instance Level +----------------------------------------------------- + +.. autofunction:: add_dependency + +.. autofunction:: get_pairwise_statement_orderings + +.. autofunction:: find_unsatisfied_dependencies + Troubleshooting --------------- From ab55b38e9646c816e6e228f3d532054fd7326d7a Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 16:17:28 -0500 Subject: [PATCH 59/62] fix merge conflicts --- doc/ref_other.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/ref_other.rst b/doc/ref_other.rst index b67705da3..9b3547421 100644 --- a/doc/ref_other.rst +++ b/doc/ref_other.rst @@ -29,17 +29,12 @@ Automatic Testing Checking Dependencies at the Statement-Instance Level ----------------------------------------------------- -<<<<<<< HEAD .. autofunction:: add_dependency .. autofunction:: get_pairwise_statement_orderings .. autofunction:: find_unsatisfied_dependencies -======= -.. autofunction:: get_pairwise_statement_orderings - ->>>>>>> statement-instance-order-and-lex-order-map Troubleshooting --------------- From 7755f6ae6cde7b8626f810eb8954fbea5ad125a5 Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 17:20:52 -0500 Subject: [PATCH 60/62] add autofunction find_unsatisfied_deps --- loopy/schedule/checker/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 9817d3899..b0a06a7e8 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -1,6 +1,8 @@ """ .. autofunction:: get_pairwise_statement_orderings +.. autofunction:: find_unsatisfied_dependencies + .. automodule:: loopy.schedule.checker.schedule """ From 68a242f80de84a5161cc0194386c8fce098a3d3b Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Fri, 17 Sep 2021 17:27:29 -0500 Subject: [PATCH 61/62] Fix up schedule checker docs --- doc/ref_other.rst | 4 +--- loopy/schedule/checker/__init__.py | 8 +++++--- loopy/schedule/checker/utils.py | 5 +++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/ref_other.rst b/doc/ref_other.rst index a06d10fae..55a72d5c2 100644 --- a/doc/ref_other.rst +++ b/doc/ref_other.rst @@ -29,12 +29,10 @@ Automatic Testing Checking Dependencies at the Statement-Instance Level ----------------------------------------------------- -.. autofunction:: add_dependency +See also :func:`~loopy.add_dependency` for how to add dependencies. .. automodule:: loopy.schedule.checker -.. autofunction:: find_unsatisfied_dependencies - Troubleshooting --------------- diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index b0a06a7e8..35d0b2fc3 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -4,6 +4,7 @@ .. autofunction:: find_unsatisfied_dependencies .. automodule:: loopy.schedule.checker.schedule +.. automodule:: loopy.schedule.checker.utils """ @@ -188,15 +189,16 @@ def find_unsatisfied_dependencies( checking dependencies once the first unsatisfied dependency is found. :returns: A list of unsatisfied dependencies, each represented as a - :class:`collections.namedtuple` containing the following: + :func:`collections.namedtuple` containing the following: - `statement_pair`: The (before, after) pair of statement IDs involved in the dependency. - `dependency`: An class:`islpy.Map` from each instance of the first statement to all instances of the second statement that must occur later. - - `statement_ordering`: A :class:`StatementOrdering` resulting from - `lp.get_pairwise_statement_orderings` (defined above). + - `statement_ordering`: A + :class:`~loopy.schedule.checker.schedule.StatementOrdering` + resulting from :func:`get_pairwise_statement_orderings` (defined above). """ diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index 55197feea..d2a3a8350 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -1,3 +1,8 @@ +""" +.. autofunction:: make_dep_map +""" + + __copyright__ = "Copyright (C) 2019 James Stevens" __license__ = """ From a887ff28d57b99b161d02b71ed2c4c2798e8abad Mon Sep 17 00:00:00 2001 From: jdsteve2 Date: Fri, 17 Sep 2021 18:14:32 -0500 Subject: [PATCH 62/62] fix reference in docstrings loopy.kernel.LoopKernel->loopy.LoopKernel --- loopy/schedule/checker/__init__.py | 2 +- loopy/schedule/checker/utils.py | 2 +- loopy/transform/instruction.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/loopy/schedule/checker/__init__.py b/loopy/schedule/checker/__init__.py index 0ce8f6dce..aa571b0e6 100644 --- a/loopy/schedule/checker/__init__.py +++ b/loopy/schedule/checker/__init__.py @@ -174,7 +174,7 @@ def find_unsatisfied_dependencies( violated by the linearization described by `lin_items`, and return these dependencies. - :arg knl: A preprocessed (or linearized) :class:`loopy.kernel.LoopKernel` + :arg knl: A preprocessed (or linearized) :class:`loopy.LoopKernel` containing the statements (:class:`loopy.InstructionBase`) whose dependencies will be checked against the linearization items. diff --git a/loopy/schedule/checker/utils.py b/loopy/schedule/checker/utils.py index d2a3a8350..12d3480e1 100644 --- a/loopy/schedule/checker/utils.py +++ b/loopy/schedule/checker/utils.py @@ -224,7 +224,7 @@ def make_dep_map(s, self_dep=False, knl_with_domains=None): If not, the value for the *output* statement identifier dimension will be set to 1. - :arg knl_with_domains: A :class:`loopy.kernel.LoopKernel` containing iname + :arg knl_with_domains: A :class:`loopy.LoopKernel` containing iname domains that will be used to constrain the inames in the dependency map. If provided, the domains for the inames found in the dependency will be intersected with their domains expressed in the kernel. diff --git a/loopy/transform/instruction.py b/loopy/transform/instruction.py index 6c05b45d0..3c3c16274 100644 --- a/loopy/transform/instruction.py +++ b/loopy/transform/instruction.py @@ -101,7 +101,7 @@ def add_dependency(kernel, insn_match, depends_on): """Add dependency contained in *depends_on* to the instructions matched by *insn_match*. - :arg kernel: A :class:`loopy.kernel.LoopKernel`. + :arg kernel: A :class:`loopy.LoopKernel`. :arg insn_match: An instruction id match understood by :func:`loopy.match.parse_match` identifying the statement to which the @@ -115,7 +115,7 @@ def add_dependency(kernel, insn_match, depends_on): instruction id match identifying dependee statement ids to be added to `stmt.depends_on` for any stmt matching *insn_match*. - :returns: The updated :class:`loopy.kernel.LoopKernel` with the new dependency. + :returns: The updated :class:`loopy.LoopKernel` with the new dependency. .. versionchanged:: 2016.3