Skip to content

Commit f2b7e82

Browse files
authored
[microNPU][3] Plan generation for the cascader (#9890)
* [microNPU][3] Plan generation for the cascader The cascader creates 'Plans' which describe how to schedule subgraphs. As part of the cascading algorithm, it's necessary to explore a large variety of Plans which are Pareto optimal (in terms of memory usage and performance). This is done by the Plan generation algorithm. This commit adds the TensorConfig and Plan data structures which hold information on how to schedule the tensors/operators. Additionally, it includes functions to calculate Pareto frontiers which are used to cull sub-optimal Plans. Change-Id: Ia358b2a1b29bd810df4441027752ced75812ad4e * Fixes to lint/test Change-Id: If4e083a3c96af75a8ffa72510704818d21a477d9 * Improve python docs Change-Id: I831137f8235665bc20ab4c060cc7049ffd48088a * Fix enum hashing issue with old gcc Change-Id: Ifbe97eb33b1ef313710f24c687a8155421a3c195
1 parent 9518b7e commit f2b7e82

File tree

22 files changed

+3090
-95
lines changed

22 files changed

+3090
-95
lines changed

python/tvm/contrib/ethosu/cascader/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@
3434
)
3535
from .parts import InlinePart, EthosuPart
3636
from .device_config import EthosuDeviceConfig
37+
from .tensor_config import TensorConfigState, MemoryRegion, TensorConfig
38+
from .plan import Plan
39+
from .cascader_options import CascaderOptions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
"""Object to hold options for the NPU cascader"""
18+
import tvm._ffi
19+
20+
from tvm.runtime import Object
21+
22+
from . import _ffi_api
23+
from .tensor_config import MemoryRegion
24+
25+
26+
@tvm._ffi.register_object("contrib.ethosu.cascader.CascaderOptions")
27+
class CascaderOptions(Object):
28+
"""
29+
A class to hold configuration options for the cascader.
30+
31+
Attributes
32+
----------
33+
cascade_region : MemoryRegion
34+
The MemoryRegion to place cascading buffers into.
35+
max_proposals : int
36+
The maximum number of Proposals to generate.
37+
stripe_factors : int
38+
How many striping factors to try per axis.
39+
max_plan_size : int
40+
The maximum number of Parts in a Plan.
41+
always_copy_size : int
42+
The maximum size of a Tensor that will always be copied into the cascade region.
43+
44+
"""
45+
46+
def __init__(
47+
self,
48+
cascade_region: MemoryRegion,
49+
max_proposals: int,
50+
stripe_factors: int,
51+
max_plan_size: int,
52+
always_copy_size: int,
53+
):
54+
self.__init_handle_by_constructor__(
55+
_ffi_api.CascaderOptions,
56+
cascade_region,
57+
max_proposals,
58+
stripe_factors,
59+
max_plan_size,
60+
always_copy_size,
61+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
"""Pareto optimisation functions for the NPU cascader."""
18+
from typing import List
19+
20+
from tvm import Object
21+
22+
from . import _ffi_api
23+
from .plan import Plan
24+
25+
26+
def _get_pareto_frontier(costs: List[List[float]]) -> List[bool]:
27+
for i, cost in enumerate(costs):
28+
for j, value in enumerate(cost):
29+
costs[i][j] = float(value)
30+
31+
return [bool(v) for v in _ffi_api.GetParetoFrontier(costs)]
32+
33+
34+
def _thin_vector(vec: List[Object], max_size: int) -> List[Object]:
35+
return list(_ffi_api.ThinVector(vec, max_size))
36+
37+
38+
def _pareto_cull_plans(plans: List[Plan], max_plans: int) -> List[Plan]:
39+
return list(_ffi_api.ParetoCullPlans(plans, max_plans))
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
"""Plan class to hold subgraph scheduling information."""
18+
from typing import Dict, FrozenSet
19+
import tvm._ffi
20+
21+
from tvm.runtime import Object
22+
23+
from . import _ffi_api
24+
from .graph import Tensor, Part
25+
from .tensor_config import TensorConfig, MemoryRegion
26+
27+
28+
@tvm._ffi.register_object("contrib.ethosu.cascader.Plan")
29+
class Plan(Object):
30+
"""
31+
A class which describes how to schedule a subgraph of Parts together.
32+
33+
A Plan takes the form of a subgraph of connected Parts (recorded in part_group) with
34+
TensorConfigs for all of the required Tensors (recorded in tensor_configs). This information
35+
can be used to produce a Tensor Expression schedule with inter-operator scheduling. A Plan is
36+
necessarily single-output such that all non-output Parts are 'computed_at'ed the scope of the
37+
output Part. This is what achieves the technique referred to as 'cascading'. A Plan also has
38+
an interior memory region which specifies the region of memory into which all the Plans
39+
intermediate buffers should be allocated.
40+
41+
Additionally, a Plan contains some other information used during the Plan generation and
42+
selection algorithms. Both the memory and cycles required to run the Plan are accounted for so
43+
that Plans can be ranked and Pareto-culled on these metrics. Furthermore, the TensorConfigs
44+
which are 'open' is recorded indicating that these are valid points to merge with another Plan.
45+
A Plan can only be turned into a schedule if it has no 'open' TensorConfigs - at which point
46+
the Plan is said to be 'closed'.
47+
48+
Attributes
49+
----------
50+
tensor_configs : Dict[Tensor, TensorConfig]
51+
The TensorConfigs specified by the Plan.
52+
open_configs : FrozenSet[TensorConfig]
53+
The TensorConfigs which are 'open' meaning they are a Plan input/output but have
54+
'interior' state.
55+
output_config : TensorConfig
56+
The TensorConfig of the Plan's output tensor.
57+
part_group : FrozenSet[Part]
58+
The Parts which are covered by the Plan.
59+
interior_region : MemoryRegion
60+
The MemoryRegion in which to store 'interior' Plan buffers.
61+
memory_usage : int
62+
The interior memory used by the Plan in bytes.
63+
cycles : int
64+
The cycles taken to execute the Plan.
65+
66+
"""
67+
68+
def __init__(
69+
self,
70+
tensor_configs: Dict[Tensor, TensorConfig],
71+
open_configs: FrozenSet[TensorConfig],
72+
output_config: TensorConfig,
73+
part_group: FrozenSet[Part],
74+
interior_region: MemoryRegion,
75+
memory_usage: int,
76+
cycles: int,
77+
):
78+
self.__init_handle_by_constructor__(
79+
_ffi_api.Plan,
80+
list(tensor_configs.values()),
81+
list(open_configs),
82+
output_config,
83+
list(part_group),
84+
interior_region,
85+
memory_usage,
86+
cycles,
87+
)
88+
89+
def merge(self, other):
90+
"""
91+
Merge two Plans with share an 'open' TensorConfig.
92+
93+
The current Plan is referred to as the 'upper Plan' and the other Plan as the 'lower
94+
Plan'. The 'open' output config of the upper Plan must be an 'open' input config of the
95+
lower Plan. The Tensor referenced by these configs is the Tensor on which the two Plans
96+
will be merged. The merge process does the following:
97+
98+
The tensor config maps will be merged with TensorConfigs from the upper Plan taking
99+
priority. The open configs will be merged with the TensorConfigs that are being merged
100+
having been removed. The output config will be that of the lower Plan. The part groups
101+
will be merged. The interior region is necessarily the same for both the upper and lower
102+
Plan. The cycles and memory usage will be summed.
103+
104+
Parameters
105+
----------
106+
other : Plan
107+
The Plan to merge with.
108+
109+
Return
110+
------
111+
Plan
112+
The merged Plan.
113+
114+
"""
115+
return _ffi_api.PlanMerge(self, other)
116+
117+
@property
118+
def tensor_configs(self):
119+
"""The TensorConfigs specified by the Plan."""
120+
tensor_configs = {}
121+
for config in self._tensor_configs:
122+
tensor_configs[config.tensor] = config
123+
return tensor_configs
124+
125+
@property
126+
def open_configs(self):
127+
"""
128+
The TensorConfigs which are 'open' meaning they are a Plan input/output but have
129+
'interior' state.
130+
"""
131+
return frozenset(self._open_configs)
132+
133+
@property
134+
def output_config(self):
135+
"""The TensorConfig of the Plan's output tensor."""
136+
return self._output_config
137+
138+
@property
139+
def part_group(self):
140+
"""The Parts which are covered by the Plan."""
141+
return frozenset(self._part_group)
142+
143+
@property
144+
def interior_region(self):
145+
"""The MemoryRegion in which to store 'interior' Plan buffers."""
146+
return self._interior_region
147+
148+
@property
149+
def memory_usage(self):
150+
"""The interior memory used by the Plan in bytes."""
151+
return self._memory_usage
152+
153+
@property
154+
def cycles(self):
155+
"""The cycles taken to execute the Plan."""
156+
return self._cycles
157+
158+
def __repr__(self):
159+
return (
160+
f"Plan(tensor_configs={self.tensor_configs}, "
161+
f"open_configs={self.open_configs}, "
162+
f"output_config={self.output_config}, "
163+
f"part_group={self.part_group}, "
164+
f"interior_region={self.interior_region.name}, "
165+
f"memory_usage={self.memory_usage}, "
166+
f"cycles={self.cycles}, "
167+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
"""Algorithms to generate Plans for a CascaderGraph."""
18+
from typing import List, Dict
19+
20+
from tvm.contrib.ethosu.cascader.tensor_config import MemoryRegion
21+
22+
from . import _ffi_api
23+
from .cascader_options import CascaderOptions
24+
from .plan import Plan
25+
from .stripe_config import StripeConfig
26+
from .graph import CascaderGraph, Part, Tensor
27+
28+
29+
def _generate_output_stripe_configs(part: Part, stripe_factors: int) -> List[StripeConfig]:
30+
return list(_ffi_api.GenerateOutputStripeConfigs(part, stripe_factors))
31+
32+
33+
def _generate_single_plans(
34+
part: Part,
35+
output_stripe_configs: List[StripeConfig],
36+
home_map: Dict[Tensor, List[MemoryRegion]],
37+
cascade_region: MemoryRegion,
38+
) -> List[Plan]:
39+
return list(_ffi_api.GenerateSinglePlans(part, output_stripe_configs, home_map, cascade_region))
40+
41+
42+
def _generate_graph_plans(
43+
graph: CascaderGraph,
44+
home_map: Dict[Tensor, List[MemoryRegion]],
45+
options: CascaderOptions,
46+
):
47+
return _ffi_api.GenerateGraphPlans(
48+
graph,
49+
home_map,
50+
options,
51+
)

0 commit comments

Comments
 (0)