|
23 | 23 |
|
24 | 24 | from pytools import ImmutableRecord |
25 | 25 | import sys |
| 26 | +import islpy as isl |
26 | 27 | from loopy.diagnostic import warn_with_kernel, LoopyError # noqa |
27 | 28 |
|
28 | 29 | from pytools import MinRecursionLimit, ProcessLogger |
@@ -214,24 +215,40 @@ def find_loop_nest_around_map(kernel): |
214 | 215 | """Returns a dictionary mapping inames to other inames that are |
215 | 216 | always nested around them. |
216 | 217 | """ |
217 | | - from collections import defaultdict |
218 | | - from loopy.schedule.tools import get_loop_nest_tree |
| 218 | + result = {} |
| 219 | + |
| 220 | + all_inames = kernel.all_inames() |
219 | 221 |
|
220 | | - tree = get_loop_nest_tree(kernel) |
| 222 | + iname_to_insns = kernel.iname_to_insns() |
221 | 223 |
|
222 | | - loop_nest_around_map = defaultdict(frozenset) |
| 224 | + # examine pairs of all inames--O(n**2), I know. |
| 225 | + from loopy.kernel.data import IlpBaseTag |
| 226 | + for inner_iname in all_inames: |
| 227 | + result[inner_iname] = set() |
| 228 | + for outer_iname in all_inames: |
| 229 | + if inner_iname == outer_iname: |
| 230 | + continue |
223 | 231 |
|
224 | | - for node in tree.all_nodes_itr(): |
225 | | - if node.identifier == tree.root: |
226 | | - continue |
227 | | - iname = node.identifier |
228 | | - depth = tree.depth(iname) |
229 | | - all_ancestors = frozenset(tree.ancestor(iname, d).identifier |
230 | | - for d in range(1, depth)) |
| 232 | + if kernel.iname_tags_of_type(outer_iname, IlpBaseTag): |
| 233 | + # ILP tags are special because they are parallel tags |
| 234 | + # and therefore 'in principle' nest around everything. |
| 235 | + # But they're realized by the scheduler as a loop |
| 236 | + # at the innermost level, so we'll cut them some |
| 237 | + # slack here. |
| 238 | + continue |
| 239 | + |
| 240 | + if iname_to_insns[inner_iname] < iname_to_insns[outer_iname]: |
| 241 | + result[inner_iname].add(outer_iname) |
| 242 | + |
| 243 | + for dom_idx, dom in enumerate(kernel.domains): |
| 244 | + for outer_iname in dom.get_var_names(isl.dim_type.param): |
| 245 | + if outer_iname not in all_inames: |
| 246 | + continue |
231 | 247 |
|
232 | | - loop_nest_around_map[iname] = all_ancestors |
| 248 | + for inner_iname in dom.get_var_names(isl.dim_type.set): |
| 249 | + result[inner_iname].add(outer_iname) |
233 | 250 |
|
234 | | - return loop_nest_around_map |
| 251 | + return result |
235 | 252 |
|
236 | 253 |
|
237 | 254 | def find_loop_insn_dep_map(kernel, loop_nest_with_map, loop_nest_around_map): |
@@ -802,17 +819,24 @@ def _get_dep_equivalent_nests(tree, within1, within2): |
802 | 819 | return iname1, iname2 |
803 | 820 |
|
804 | 821 |
|
| 822 | +class V2SchedulerNotImplementedException(RuntimeError): |
| 823 | + pass |
| 824 | + |
| 825 | + |
805 | 826 | def generate_loop_schedules_v2(kernel): |
806 | 827 | from loopy.schedule.tools import get_loop_nest_tree |
807 | 828 | from functools import reduce |
808 | 829 | from pytools.graph import compute_topological_order |
809 | 830 | from loopy.kernel.data import ConcurrentTag, IlpBaseTag, VectorizeTag |
810 | 831 |
|
811 | 832 | if any(insn.priority != 0 for insn in kernel.instructions): |
812 | | - raise NotImplementedError |
| 833 | + raise V2SchedulerNotImplementedException("v2 scheduler cannot schedule" |
| 834 | + " kernels with instruction priorities set.") |
813 | 835 |
|
814 | 836 | if kernel.schedule is not None: |
815 | | - raise NotImplementedError |
| 837 | + # cannnot handle preschedule yet |
| 838 | + raise V2SchedulerNotImplementedException("v2 scheduler cannot schedule" |
| 839 | + " prescheduled kernels.") |
816 | 840 |
|
817 | 841 | concurrent_inames = {iname for iname in kernel.all_inames() |
818 | 842 | if kernel.iname_tags_of_type(iname, ConcurrentTag)} |
@@ -2074,154 +2098,147 @@ def generate_loop_schedules_inner(kernel, debug_args={}): |
2074 | 2098 | from loopy.check import pre_schedule_checks |
2075 | 2099 | pre_schedule_checks(kernel) |
2076 | 2100 |
|
2077 | | - can_v2_scheduler_handle = ( |
2078 | | - # v2-scheduler cannot handle insn groups |
2079 | | - all(len(insn.conflicts_with_groups) == 0 |
2080 | | - for insn in kernel.instructions) |
2081 | | - # v2-scheduler cannot handle prescheduled kernel |
2082 | | - and (not kernel.schedule) |
2083 | | - # v2-scheduler cannot handle instruction priorities |
2084 | | - and all(insn.priority == 0 |
2085 | | - for insn in kernel.instructions) |
2086 | | - ) |
2087 | | - |
2088 | | - if can_v2_scheduler_handle: |
| 2101 | + try: |
2089 | 2102 | gen_sched = generate_loop_schedules_v2(kernel) |
2090 | 2103 | yield postprocess_schedule(kernel, gen_sched) |
2091 | | - else: |
2092 | | - schedule_count = 0 |
2093 | | - |
2094 | | - debug = ScheduleDebugger(**debug_args) |
2095 | | - |
2096 | | - preschedule = (kernel.schedule |
2097 | | - |
2098 | | - if kernel.state == KernelState.LINEARIZED |
2099 | | - |
2100 | | - else ()) |
2101 | | - |
2102 | | - prescheduled_inames = { |
2103 | | - insn.iname |
2104 | | - for insn in preschedule |
2105 | | - if isinstance(insn, EnterLoop)} |
2106 | | - |
2107 | | - prescheduled_insn_ids = { |
2108 | | - insn_id |
2109 | | - for item in preschedule |
2110 | | - for insn_id in sched_item_to_insn_id(item)} |
2111 | | - |
2112 | | - from loopy.kernel.data import (IlpBaseTag, ConcurrentTag, VectorizeTag, |
2113 | | - filter_iname_tags_by_type) |
2114 | | - ilp_inames = { |
2115 | | - name |
2116 | | - for name, iname in kernel.inames.items() |
2117 | | - if filter_iname_tags_by_type(iname.tags, IlpBaseTag)} |
2118 | | - vec_inames = { |
2119 | | - name |
2120 | | - for name, iname in kernel.inames.items() |
2121 | | - if filter_iname_tags_by_type(iname.tags, VectorizeTag)} |
2122 | | - parallel_inames = { |
2123 | | - name |
2124 | | - for name, iname in kernel.inames.items() |
2125 | | - if filter_iname_tags_by_type(iname.tags, ConcurrentTag)} |
2126 | | - |
2127 | | - loop_nest_with_map = find_loop_nest_with_map(kernel) |
2128 | | - loop_nest_around_map = find_loop_nest_around_map(kernel) |
2129 | | - sched_state = SchedulerState( |
2130 | | - kernel=kernel, |
2131 | | - loop_nest_around_map=loop_nest_around_map, |
2132 | | - loop_insn_dep_map=find_loop_insn_dep_map( |
2133 | | - kernel, |
2134 | | - loop_nest_with_map=loop_nest_with_map, |
2135 | | - loop_nest_around_map=loop_nest_around_map), |
2136 | | - breakable_inames=ilp_inames, |
2137 | | - ilp_inames=ilp_inames, |
2138 | | - vec_inames=vec_inames, |
2139 | | - |
2140 | | - prescheduled_inames=prescheduled_inames, |
2141 | | - prescheduled_insn_ids=prescheduled_insn_ids, |
2142 | | - |
2143 | | - # time-varying part |
2144 | | - active_inames=(), |
2145 | | - entered_inames=frozenset(), |
2146 | | - enclosing_subkernel_inames=(), |
2147 | | - |
2148 | | - schedule=(), |
2149 | | - |
2150 | | - unscheduled_insn_ids={insn.id for insn in kernel.instructions}, |
2151 | | - scheduled_insn_ids=frozenset(), |
2152 | | - within_subkernel=kernel.state != KernelState.LINEARIZED, |
2153 | | - may_schedule_global_barriers=True, |
2154 | | - |
2155 | | - preschedule=preschedule, |
2156 | | - insn_ids_to_try=None, |
2157 | | - |
2158 | | - # ilp and vec are not parallel for the purposes of the scheduler |
2159 | | - parallel_inames=parallel_inames - ilp_inames - vec_inames, |
2160 | | - |
2161 | | - group_insn_counts=group_insn_counts(kernel), |
2162 | | - active_group_counts={}, |
2163 | | - |
2164 | | - insns_in_topologically_sorted_order=( |
2165 | | - get_insns_in_topologically_sorted_order(kernel)), |
2166 | | - ) |
2167 | | - |
2168 | | - schedule_gen_kwargs = {} |
2169 | | - |
2170 | | - def print_longest_dead_end(): |
2171 | | - if debug.interactive: |
2172 | | - print("Loopy will now show you the scheduler state at the point") |
2173 | | - print("where the longest (dead-end) schedule was generated, in the") |
2174 | | - print("the hope that some of this makes sense and helps you find") |
2175 | | - print("the issue.") |
2176 | | - print() |
2177 | | - print("To disable this interactive behavior, pass") |
2178 | | - print(" debug_args=dict(interactive=False)") |
2179 | | - print("to generate_loop_schedules().") |
2180 | | - print(75*"-") |
2181 | | - input("Enter:") |
2182 | | - print() |
2183 | | - print() |
2184 | | - |
2185 | | - debug.debug_length = len(debug.longest_rejected_schedule) |
2186 | | - while True: |
2187 | | - try: |
2188 | | - for _ in generate_loop_schedules_internal( |
2189 | | - sched_state, debug=debug, **schedule_gen_kwargs): |
2190 | | - pass |
2191 | | - |
2192 | | - except ScheduleDebugInput as e: |
2193 | | - debug.debug_length = int(str(e)) |
2194 | | - continue |
| 2104 | + return |
| 2105 | + except V2SchedulerNotImplementedException as e: |
| 2106 | + from warnings import warn |
| 2107 | + warn(f"Falling back to a slow scheduler implementation due to: {e}") |
2195 | 2108 |
|
2196 | | - break |
| 2109 | + schedule_count = 0 |
2197 | 2110 |
|
2198 | | - try: |
2199 | | - for gen_sched in generate_loop_schedules_internal( |
2200 | | - sched_state, debug=debug, **schedule_gen_kwargs): |
2201 | | - debug.stop() |
| 2111 | + debug = ScheduleDebugger(**debug_args) |
| 2112 | + |
| 2113 | + preschedule = (kernel.schedule |
| 2114 | + |
| 2115 | + if kernel.state == KernelState.LINEARIZED |
| 2116 | + |
| 2117 | + else ()) |
| 2118 | + |
| 2119 | + prescheduled_inames = { |
| 2120 | + insn.iname |
| 2121 | + for insn in preschedule |
| 2122 | + if isinstance(insn, EnterLoop)} |
| 2123 | + |
| 2124 | + prescheduled_insn_ids = { |
| 2125 | + insn_id |
| 2126 | + for item in preschedule |
| 2127 | + for insn_id in sched_item_to_insn_id(item)} |
| 2128 | + |
| 2129 | + from loopy.kernel.data import (IlpBaseTag, ConcurrentTag, VectorizeTag, |
| 2130 | + filter_iname_tags_by_type) |
| 2131 | + ilp_inames = { |
| 2132 | + name |
| 2133 | + for name, iname in kernel.inames.items() |
| 2134 | + if filter_iname_tags_by_type(iname.tags, IlpBaseTag)} |
| 2135 | + vec_inames = { |
| 2136 | + name |
| 2137 | + for name, iname in kernel.inames.items() |
| 2138 | + if filter_iname_tags_by_type(iname.tags, VectorizeTag)} |
| 2139 | + parallel_inames = { |
| 2140 | + name |
| 2141 | + for name, iname in kernel.inames.items() |
| 2142 | + if filter_iname_tags_by_type(iname.tags, ConcurrentTag)} |
| 2143 | + |
| 2144 | + loop_nest_with_map = find_loop_nest_with_map(kernel) |
| 2145 | + loop_nest_around_map = find_loop_nest_around_map(kernel) |
| 2146 | + sched_state = SchedulerState( |
| 2147 | + kernel=kernel, |
| 2148 | + loop_nest_around_map=loop_nest_around_map, |
| 2149 | + loop_insn_dep_map=find_loop_insn_dep_map( |
| 2150 | + kernel, |
| 2151 | + loop_nest_with_map=loop_nest_with_map, |
| 2152 | + loop_nest_around_map=loop_nest_around_map), |
| 2153 | + breakable_inames=ilp_inames, |
| 2154 | + ilp_inames=ilp_inames, |
| 2155 | + vec_inames=vec_inames, |
| 2156 | + |
| 2157 | + prescheduled_inames=prescheduled_inames, |
| 2158 | + prescheduled_insn_ids=prescheduled_insn_ids, |
| 2159 | + |
| 2160 | + # time-varying part |
| 2161 | + active_inames=(), |
| 2162 | + entered_inames=frozenset(), |
| 2163 | + enclosing_subkernel_inames=(), |
| 2164 | + |
| 2165 | + schedule=(), |
| 2166 | + |
| 2167 | + unscheduled_insn_ids={insn.id for insn in kernel.instructions}, |
| 2168 | + scheduled_insn_ids=frozenset(), |
| 2169 | + within_subkernel=kernel.state != KernelState.LINEARIZED, |
| 2170 | + may_schedule_global_barriers=True, |
| 2171 | + |
| 2172 | + preschedule=preschedule, |
| 2173 | + insn_ids_to_try=None, |
2202 | 2174 |
|
2203 | | - new_kernel = postprocess_schedule(kernel, gen_sched) |
2204 | | - yield new_kernel |
| 2175 | + # ilp and vec are not parallel for the purposes of the scheduler |
| 2176 | + parallel_inames=parallel_inames - ilp_inames - vec_inames, |
2205 | 2177 |
|
2206 | | - debug.start() |
| 2178 | + group_insn_counts=group_insn_counts(kernel), |
| 2179 | + active_group_counts={}, |
2207 | 2180 |
|
2208 | | - schedule_count += 1 |
| 2181 | + insns_in_topologically_sorted_order=( |
| 2182 | + get_insns_in_topologically_sorted_order(kernel)), |
| 2183 | + ) |
2209 | 2184 |
|
2210 | | - except KeyboardInterrupt: |
| 2185 | + schedule_gen_kwargs = {} |
| 2186 | + |
| 2187 | + def print_longest_dead_end(): |
| 2188 | + if debug.interactive: |
| 2189 | + print("Loopy will now show you the scheduler state at the point") |
| 2190 | + print("where the longest (dead-end) schedule was generated, in the") |
| 2191 | + print("the hope that some of this makes sense and helps you find") |
| 2192 | + print("the issue.") |
2211 | 2193 | print() |
| 2194 | + print("To disable this interactive behavior, pass") |
| 2195 | + print(" debug_args=dict(interactive=False)") |
| 2196 | + print("to generate_loop_schedules().") |
2212 | 2197 | print(75*"-") |
2213 | | - print("Interrupted during scheduling") |
2214 | | - print(75*"-") |
2215 | | - print_longest_dead_end() |
2216 | | - raise |
| 2198 | + input("Enter:") |
| 2199 | + print() |
| 2200 | + print() |
2217 | 2201 |
|
2218 | | - debug.done_scheduling() |
2219 | | - if not schedule_count: |
2220 | | - print(75*"-") |
2221 | | - print("ERROR: Sorry--loopy did not find a schedule for your kernel.") |
2222 | | - print(75*"-") |
2223 | | - print_longest_dead_end() |
2224 | | - raise RuntimeError("no valid schedules found") |
| 2202 | + debug.debug_length = len(debug.longest_rejected_schedule) |
| 2203 | + while True: |
| 2204 | + try: |
| 2205 | + for _ in generate_loop_schedules_internal( |
| 2206 | + sched_state, debug=debug, **schedule_gen_kwargs): |
| 2207 | + pass |
| 2208 | + |
| 2209 | + except ScheduleDebugInput as e: |
| 2210 | + debug.debug_length = int(str(e)) |
| 2211 | + continue |
| 2212 | + |
| 2213 | + break |
| 2214 | + |
| 2215 | + try: |
| 2216 | + for gen_sched in generate_loop_schedules_internal( |
| 2217 | + sched_state, debug=debug, **schedule_gen_kwargs): |
| 2218 | + debug.stop() |
| 2219 | + |
| 2220 | + new_kernel = postprocess_schedule(kernel, gen_sched) |
| 2221 | + yield new_kernel |
| 2222 | + |
| 2223 | + debug.start() |
| 2224 | + |
| 2225 | + schedule_count += 1 |
| 2226 | + |
| 2227 | + except KeyboardInterrupt: |
| 2228 | + print() |
| 2229 | + print(75*"-") |
| 2230 | + print("Interrupted during scheduling") |
| 2231 | + print(75*"-") |
| 2232 | + print_longest_dead_end() |
| 2233 | + raise |
| 2234 | + |
| 2235 | + debug.done_scheduling() |
| 2236 | + if not schedule_count: |
| 2237 | + print(75*"-") |
| 2238 | + print("ERROR: Sorry--loopy did not find a schedule for your kernel.") |
| 2239 | + print(75*"-") |
| 2240 | + print_longest_dead_end() |
| 2241 | + raise RuntimeError("no valid schedules found") |
2225 | 2242 |
|
2226 | 2243 | logger.info("%s: schedule done" % kernel.name) |
2227 | 2244 |
|
|
0 commit comments