From 09acbe8f5d63fe8bfccd7aa8fd49c84523c38ec1 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Wed, 18 Oct 2017 11:54:38 -0500 Subject: [PATCH 1/5] Make printed kernels more like input language --- loopy/kernel/__init__.py | 123 +----------------------------- loopy/kernel/tools.py | 159 ++++++++++++++++++++++++++++++++++++++- loopy/tools.py | 15 ++++ 3 files changed, 175 insertions(+), 122 deletions(-) diff --git a/loopy/kernel/__init__.py b/loopy/kernel/__init__.py index 642c82c4b..24f0ebb14 100644 --- a/loopy/kernel/__init__.py +++ b/loopy/kernel/__init__.py @@ -40,6 +40,7 @@ from loopy.library.function import ( single_arg_function_mangler) from loopy.diagnostic import CannotBranchDomainTree, LoopyError +from loopy.tools import natsorted # {{{ unique var names @@ -1128,20 +1129,6 @@ class LoopKernel(ImmutableRecordWithoutPickling): else: sep = [] - def natorder(key): - # Return natural ordering for strings, as opposed to dictionary order. - # E.g. will result in - # 'abc1' < 'abc9' < 'abc10' - # rather than - # 'abc1' < 'abc10' < 'abc9' - # Based on - # http://code.activestate.com/recipes/285264-natural-string-sorting/#c7 - import re - return [int(n) if n else s for n, s in re.findall(r'(\d+)|(\D+)', key)] - - def natsorted(seq, key=lambda x: x): - return sorted(seq, key=lambda y: natorder(key(y))) - if "name" in what: lines.extend(sep) lines.append("KERNEL: " + kernel.name) @@ -1187,113 +1174,9 @@ class LoopKernel(ImmutableRecordWithoutPickling): lines.extend(sep) if show_labels: lines.append("INSTRUCTIONS:") - loop_list_width = 35 - - # {{{ topological sort - - printed_insn_ids = set() - printed_insn_order = [] - - def insert_insn_into_order(insn): - if insn.id in printed_insn_ids: - return - printed_insn_ids.add(insn.id) - - for dep_id in natsorted(insn.depends_on): - insert_insn_into_order(kernel.id_to_insn[dep_id]) - - printed_insn_order.append(insn) - - for insn in kernel.instructions: - insert_insn_into_order(insn) - - # }}} - - import loopy as lp - - Fore = self.options._fore # noqa - Style = self.options._style # noqa - - from loopy.kernel.tools import draw_dependencies_as_unicode_arrows - for insn, (arrows, extender) in zip( - printed_insn_order, - draw_dependencies_as_unicode_arrows( - printed_insn_order, fore=Fore, style=Style)): - - if isinstance(insn, lp.MultiAssignmentBase): - lhs = ", ".join(str(a) for a in insn.assignees) - rhs = str(insn.expression) - trailing = [] - elif isinstance(insn, lp.CInstruction): - lhs = ", ".join(str(a) for a in insn.assignees) - rhs = "CODE(%s|%s)" % ( - ", ".join(str(x) for x in insn.read_variables), - ", ".join("%s=%s" % (name, expr) - for name, expr in insn.iname_exprs)) - - trailing = [" "+l for l in insn.code.split("\n")] - elif isinstance(insn, lp.BarrierInstruction): - lhs = "" - rhs = "... %sbarrier" % insn.kind[0] - trailing = [] - - elif isinstance(insn, lp.NoOpInstruction): - lhs = "" - rhs = "... nop" - trailing = [] - - else: - raise LoopyError("unexpected instruction type: %s" - % type(insn).__name__) - - order = self._get_iname_order_for_printing() - loop_list = ",".join( - sorted(kernel.insn_inames(insn), key=lambda iname: order[iname])) - - options = [Fore.GREEN+insn.id+Style.RESET_ALL] - if insn.priority: - options.append("priority=%d" % insn.priority) - if insn.tags: - options.append("tags=%s" % ":".join(insn.tags)) - if isinstance(insn, lp.Assignment) and insn.atomicity: - options.append("atomic=%s" % ":".join( - str(a) for a in insn.atomicity)) - if insn.groups: - options.append("groups=%s" % ":".join(insn.groups)) - if insn.conflicts_with_groups: - options.append( - "conflicts=%s" % ":".join(insn.conflicts_with_groups)) - if insn.no_sync_with: - options.append("no_sync_with=%s" % ":".join( - "%s@%s" % entry for entry in sorted(insn.no_sync_with))) - - if lhs: - core = "%s <- %s" % ( - Fore.CYAN+lhs+Style.RESET_ALL, - Fore.MAGENTA+rhs+Style.RESET_ALL, - ) - else: - core = Fore.MAGENTA+rhs+Style.RESET_ALL - - if len(loop_list) > loop_list_width: - lines.append("%s [%s]" % (arrows, loop_list)) - lines.append("%s %s%s # %s" % ( - extender, - (loop_list_width+2)*" ", - core, - ", ".join(options))) - else: - lines.append("%s [%s]%s%s # %s" % ( - arrows, - loop_list, " "*(loop_list_width-len(loop_list)), - core, - ",".join(options))) - - lines.extend(trailing) - if insn.predicates: - lines.append(10*" " + "if (%s)" % " && ".join( - [str(x) for x in insn.predicates])) + from loopy.kernel.tools import stringify_instruction_list + lines.extend(stringify_instruction_list(kernel)) dep_lines = [] for insn in kernel.instructions: diff --git a/loopy/kernel/tools.py b/loopy/kernel/tools.py index 15840180b..ed6c55e25 100644 --- a/loopy/kernel/tools.py +++ b/loopy/kernel/tools.py @@ -35,7 +35,7 @@ import islpy as isl from islpy import dim_type from loopy.diagnostic import LoopyError, warn_with_kernel from pytools import memoize_on_first_arg - +from loopy.tools import natsorted import logging logger = logging.getLogger(__name__) @@ -1371,7 +1371,162 @@ def draw_dependencies_as_unicode_arrows( conform_to_uniform_length(extender)) for row, extender in rows] - return rows + return uniform_length, rows + +# }}} + + +# {{{ stringify_instruction_list + +def stringify_instruction_list(kernel): + # {{{ topological sort + + printed_insn_ids = set() + printed_insn_order = [] + + def insert_insn_into_order(insn): + if insn.id in printed_insn_ids: + return + printed_insn_ids.add(insn.id) + + for dep_id in natsorted(insn.depends_on): + insert_insn_into_order(kernel.id_to_insn[dep_id]) + + printed_insn_order.append(insn) + + for insn in kernel.instructions: + insert_insn_into_order(insn) + + # }}} + + import loopy as lp + + Fore = kernel.options._fore # noqa + Style = kernel.options._style # noqa + + uniform_length, arrows_and_extenders = draw_dependencies_as_unicode_arrows( + printed_insn_order, fore=Fore, style=Style) + + leader = " " * uniform_length + lines = [] + current_inames = [set()] + + indent_level = [0] + indent_increment = 2 + + iname_order = kernel._get_iname_order_for_printing() + + def add_pre_line(s): + lines.append(leader + " " * (indent_level[0] + 1) + s) + + def add_main_line(s): + lines.append(arrows + " " * (indent_level[0] + 1) + s) + + def add_post_line(s): + lines.append(extender + " " * (indent_level[0] + 1) + s) + + def adapt_to_new_inames_list(new_inames): + added = [] + removed = [] + + # FIXME: Doesn't respect strict nesting + for iname in iname_order: + is_in_current = iname in current_inames[0] + is_in_new = iname in new_inames + + if is_in_new == is_in_current: + pass + elif is_in_new and not is_in_current: + added.append(iname) + elif not is_in_new and is_in_current: + removed.append(iname) + else: + assert False + + if removed: + indent_level[0] -= indent_increment * len(removed) + add_pre_line("end " + ", ".join(removed)) + if added: + add_pre_line("for " + ", ".join(added)) + indent_level[0] += indent_increment * len(added) + + current_inames[0] = new_inames + + for insn, (arrows, extender) in zip(printed_insn_order, arrows_and_extenders): + if isinstance(insn, lp.MultiAssignmentBase): + lhs = ", ".join(str(a) for a in insn.assignees) + rhs = str(insn.expression) + trailing = [] + elif isinstance(insn, lp.CInstruction): + lhs = ", ".join(str(a) for a in insn.assignees) + rhs = "CODE(%s|%s)" % ( + ", ".join(str(x) for x in insn.read_variables), + ", ".join("%s=%s" % (name, expr) + for name, expr in insn.iname_exprs)) + + trailing = [l for l in insn.code.split("\n")] + elif isinstance(insn, lp.BarrierInstruction): + lhs = "" + rhs = "... %sbarrier" % insn.kind[0] + trailing = [] + + elif isinstance(insn, lp.NoOpInstruction): + lhs = "" + rhs = "... nop" + trailing = [] + + else: + raise LoopyError("unexpected instruction type: %s" + % type(insn).__name__) + + adapt_to_new_inames_list(kernel.insn_inames(insn)) + + options = ["id="+Fore.GREEN+insn.id+Style.RESET_ALL] + if insn.priority: + options.append("priority=%d" % insn.priority) + if insn.tags: + options.append("tags=%s" % ":".join(insn.tags)) + if isinstance(insn, lp.Assignment) and insn.atomicity: + options.append("atomic=%s" % ":".join( + str(a) for a in insn.atomicity)) + if insn.groups: + options.append("groups=%s" % ":".join(insn.groups)) + if insn.conflicts_with_groups: + options.append( + "conflicts=%s" % ":".join(insn.conflicts_with_groups)) + if insn.no_sync_with: + options.append("no_sync_with=%s" % ":".join( + "%s@%s" % entry for entry in sorted(insn.no_sync_with))) + + if lhs: + core = "%s = %s" % ( + Fore.CYAN+lhs+Style.RESET_ALL, + Fore.MAGENTA+rhs+Style.RESET_ALL, + ) + else: + core = Fore.MAGENTA+rhs+Style.RESET_ALL + + options_str = " {%s}" % ", ".join(options) + + if insn.predicates: + # FIXME: precedence + add_pre_line("if %s" % " and ".join([str(x) for x in insn.predicates])) + indent_level[0] += indent_increment + + add_main_line(core + options_str) + + for t in trailing: + add_post_line(t) + + if insn.predicates: + indent_level[0] -= indent_increment + add_post_line("end") + + leader = extender + + adapt_to_new_inames_list([]) + + return lines # }}} diff --git a/loopy/tools.py b/loopy/tools.py index 1ebbe5c8a..d6952d547 100644 --- a/loopy/tools.py +++ b/loopy/tools.py @@ -576,4 +576,19 @@ def intern_frozenset_of_ids(fs): return frozenset(intern(s) for s in fs) +def natorder(key): + # Return natural ordering for strings, as opposed to dictionary order. + # E.g. will result in + # 'abc1' < 'abc9' < 'abc10' + # rather than + # 'abc1' < 'abc10' < 'abc9' + # Based on + # http://code.activestate.com/recipes/285264-natural-string-sorting/#c7 + import re + return [int(n) if n else s for n, s in re.findall(r'(\d+)|(\D+)', key)] + + +def natsorted(seq, key=lambda x: x): + return sorted(seq, key=lambda y: natorder(key(y))) + # vim: foldmethod=marker -- GitLab From 785115e125507cdeb4b6d6afe9aa0c09ae725cf0 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 19 Oct 2017 00:57:39 -0500 Subject: [PATCH 2/5] Kernel printing: only base-indent if there are arrows --- loopy/kernel/tools.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/loopy/kernel/tools.py b/loopy/kernel/tools.py index ed6c55e25..ad1e71e59 100644 --- a/loopy/kernel/tools.py +++ b/loopy/kernel/tools.py @@ -1404,26 +1404,31 @@ def stringify_instruction_list(kernel): Fore = kernel.options._fore # noqa Style = kernel.options._style # noqa - uniform_length, arrows_and_extenders = draw_dependencies_as_unicode_arrows( - printed_insn_order, fore=Fore, style=Style) + uniform_arrow_length, arrows_and_extenders = \ + draw_dependencies_as_unicode_arrows( + printed_insn_order, fore=Fore, style=Style) - leader = " " * uniform_length + leader = " " * uniform_arrow_length lines = [] current_inames = [set()] - indent_level = [0] + if uniform_arrow_length: + indent_level = [1] + else: + indent_level = [0] + indent_increment = 2 iname_order = kernel._get_iname_order_for_printing() def add_pre_line(s): - lines.append(leader + " " * (indent_level[0] + 1) + s) + lines.append(leader + " " * indent_level[0] + s) def add_main_line(s): - lines.append(arrows + " " * (indent_level[0] + 1) + s) + lines.append(arrows + " " * indent_level[0] + s) def add_post_line(s): - lines.append(extender + " " * (indent_level[0] + 1) + s) + lines.append(extender + " " * indent_level[0] + s) def adapt_to_new_inames_list(new_inames): added = [] -- GitLab From 92dd5fc39ec4d6c4b488fe6d246b3fea0222c580 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 19 Oct 2017 00:58:07 -0500 Subject: [PATCH 3/5] Adapt test_nosync_option_parsing to new print syntax --- test/test_loopy.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/test_loopy.py b/test/test_loopy.py index 397d4832b..704fd391f 100644 --- a/test/test_loopy.py +++ b/test/test_loopy.py @@ -2206,11 +2206,12 @@ def test_nosync_option_parsing(): """, options=lp.Options(allow_terminal_colors=False)) kernel_str = str(knl) - assert "# insn1,no_sync_with=insn1@any" in kernel_str - assert "# insn2,no_sync_with=insn1@any:insn2@any" in kernel_str - assert "# insn3,no_sync_with=insn1@local:insn2@global:insn3@any" in kernel_str - assert "# insn4,no_sync_with=insn1@local:insn2@local:insn3@local:insn5@local" in kernel_str # noqa - assert "# insn5,no_sync_with=insn1@any" in kernel_str + print(kernel_str) + assert "id=insn1, no_sync_with=insn1@any" in kernel_str + assert "id=insn2, no_sync_with=insn1@any:insn2@any" in kernel_str + assert "id=insn3, no_sync_with=insn1@local:insn2@global:insn3@any" in kernel_str + assert "id=insn4, no_sync_with=insn1@local:insn2@local:insn3@local:insn5@local" in kernel_str # noqa + assert "id=insn5, no_sync_with=insn1@any" in kernel_str def assert_barrier_between(knl, id1, id2, ignore_barriers_in_levels=()): -- GitLab From 8bed085b437c9d8b0c0291ed2f580e16d994d46a Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 19 Oct 2017 00:59:22 -0500 Subject: [PATCH 4/5] Adapt tutorial doctest to new print syntax --- doc/tutorial.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 12c058fb7..49b390108 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -122,7 +122,9 @@ always see loopy's view of a kernel by printing it. i: None --------------------------------------------------------------------------- INSTRUCTIONS: - [i] out[i] <- 2*a[i] # insn + for i + out[i] = 2*a[i] {id=insn} + end i --------------------------------------------------------------------------- You'll likely have noticed that there's quite a bit more information here -- GitLab From 078022afe0ca135890a384682e7c47ef7369ff75 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 19 Oct 2017 00:59:53 -0500 Subject: [PATCH 5/5] Fix schedule printing to be more like source syntax --- doc/tutorial.rst | 16 ++++++++-------- loopy/schedule/__init__.py | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/tutorial.rst b/doc/tutorial.rst index 49b390108..8b8538725 100644 --- a/doc/tutorial.rst +++ b/doc/tutorial.rst @@ -1214,11 +1214,11 @@ should call :func:`loopy.get_one_scheduled_kernel`: --------------------------------------------------------------------------- SCHEDULE: 0: CALL KERNEL rotate_v2(extra_args=[], extra_inames=[]) - 1: [maketmp] tmp <- arr[i_inner + i_outer*16] + 1: tmp = arr[i_inner + i_outer*16] {id=maketmp} 2: RETURN FROM KERNEL rotate_v2 - 3: ---BARRIER:global--- + 3: ... gbarrier 4: CALL KERNEL rotate_v2_0(extra_args=[], extra_inames=[]) - 5: [rotate] arr[((1 + i_inner + i_outer*16) % n)] <- tmp + 5: arr[((1 + i_inner + i_outer*16) % n)] = tmp {id=rotate} 6: RETURN FROM KERNEL rotate_v2_0 --------------------------------------------------------------------------- @@ -1252,13 +1252,13 @@ put those instructions into the schedule. --------------------------------------------------------------------------- SCHEDULE: 0: CALL KERNEL rotate_v2(extra_args=['tmp_save_slot'], extra_inames=[]) - 1: [maketmp] tmp <- arr[i_inner + i_outer*16] - 2: [tmp.save] tmp_save_slot[tmp_save_hw_dim_0_rotate_v2, tmp_save_hw_dim_1_rotate_v2] <- tmp + 1: tmp = arr[i_inner + i_outer*16] {id=maketmp} + 2: tmp_save_slot[tmp_save_hw_dim_0_rotate_v2, tmp_save_hw_dim_1_rotate_v2] = tmp {id=tmp.save} 3: RETURN FROM KERNEL rotate_v2 - 4: ---BARRIER:global--- + 4: ... gbarrier 5: CALL KERNEL rotate_v2_0(extra_args=['tmp_save_slot'], extra_inames=[]) - 6: [tmp.reload] tmp <- tmp_save_slot[tmp_reload_hw_dim_0_rotate_v2_0, tmp_reload_hw_dim_1_rotate_v2_0] - 7: [rotate] arr[((1 + i_inner + i_outer*16) % n)] <- tmp + 6: tmp = tmp_save_slot[tmp_reload_hw_dim_0_rotate_v2_0, tmp_reload_hw_dim_1_rotate_v2_0] {id=tmp.reload} + 7: arr[((1 + i_inner + i_outer*16) % n)] = tmp {id=rotate} 8: RETURN FROM KERNEL rotate_v2_0 --------------------------------------------------------------------------- diff --git a/loopy/schedule/__init__.py b/loopy/schedule/__init__.py index 7cd07cd84..abf4d799f 100644 --- a/loopy/schedule/__init__.py +++ b/loopy/schedule/__init__.py @@ -431,10 +431,10 @@ def format_insn(kernel, insn_id): from loopy.kernel.instruction import ( MultiAssignmentBase, NoOpInstruction, BarrierInstruction) if isinstance(insn, MultiAssignmentBase): - return "[%s] %s%s%s <- %s%s%s" % ( - format_insn_id(kernel, insn_id), + return "%s%s%s = %s%s%s {id=%s}" % ( Fore.CYAN, ", ".join(str(a) for a in insn.assignees), Style.RESET_ALL, - Fore.MAGENTA, str(insn.expression), Style.RESET_ALL) + Fore.MAGENTA, str(insn.expression), Style.RESET_ALL, + format_insn_id(kernel, insn_id)) elif isinstance(insn, BarrierInstruction): return "[%s] %s... %sbarrier%s" % ( format_insn_id(kernel, insn_id), @@ -456,11 +456,11 @@ def dump_schedule(kernel, schedule): from loopy.kernel.data import MultiAssignmentBase for sched_item in schedule: if isinstance(sched_item, EnterLoop): - lines.append(indent + "FOR %s" % sched_item.iname) + lines.append(indent + "for %s" % sched_item.iname) indent += " " elif isinstance(sched_item, LeaveLoop): indent = indent[:-4] - lines.append(indent + "END %s" % sched_item.iname) + lines.append(indent + "end %s" % sched_item.iname) elif isinstance(sched_item, CallKernel): lines.append(indent + "CALL KERNEL %s(extra_args=%s, extra_inames=%s)" % ( @@ -479,7 +479,7 @@ def dump_schedule(kernel, schedule): insn_str = sched_item.insn_id lines.append(indent + insn_str) elif isinstance(sched_item, Barrier): - lines.append(indent + "---BARRIER:%s---" % sched_item.kind) + lines.append(indent + "... %sbarrier" % sched_item.kind[0]) else: assert False -- GitLab