conv-allocator 3.17.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conv_allocator/__init__.py +18 -0
- conv_allocator/allocator.py +323 -0
- conv_allocator/allocator_test.py +57 -0
- conv_allocator/um_accessors.py +113 -0
- conv_allocator-3.17.1.dist-info/METADATA +7 -0
- conv_allocator-3.17.1.dist-info/RECORD +9 -0
- conv_allocator-3.17.1.dist-info/WHEEL +5 -0
- conv_allocator-3.17.1.dist-info/entry_points.txt +2 -0
- conv_allocator-3.17.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# (c) Copyright 2025 Sony Semiconductor Israel, Ltd. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This software, in source or object form (the "Software"), is the
|
|
5
|
+
# property of Sony Semiconductor Israel Ltd. (the "Company") and/or its
|
|
6
|
+
# licensors, which have all right, title and interest therein, You
|
|
7
|
+
# may use the Software only in accordance with the terms of written
|
|
8
|
+
# license agreement between you and the Company (the "License").
|
|
9
|
+
# Except as expressly stated in the License, the Company grants no
|
|
10
|
+
# licenses by implication, estoppel, or otherwise. If you are not
|
|
11
|
+
# aware of or do not agree to the License terms, you may not use,
|
|
12
|
+
# copy or modify the Software. You may use the source code of the
|
|
13
|
+
# Software only for your internal purposes and may not distribute the
|
|
14
|
+
# source code of the Software, any part thereof, or any derivative work
|
|
15
|
+
# thereof, to any third party, except pursuant to the Company's prior
|
|
16
|
+
# written consent.
|
|
17
|
+
# The Software is the confidential information of the Company.
|
|
18
|
+
# -------------------------------------------------------------------------------
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# (c) Copyright 2025 Sony Semiconductor Israel, Ltd. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This software, in source or object form (the "Software"), is the
|
|
5
|
+
# property of Sony Semiconductor Israel Ltd. (the "Company") and/or its
|
|
6
|
+
# licensors, which have all right, title and interest therein, You
|
|
7
|
+
# may use the Software only in accordance with the terms of written
|
|
8
|
+
# license agreement between you and the Company (the "License").
|
|
9
|
+
# Except as expressly stated in the License, the Company grants no
|
|
10
|
+
# licenses by implication, estoppel, or otherwise. If you are not
|
|
11
|
+
# aware of or do not agree to the License terms, you may not use,
|
|
12
|
+
# copy or modify the Software. You may use the source code of the
|
|
13
|
+
# Software only for your internal purposes and may not distribute the
|
|
14
|
+
# source code of the Software, any part thereof, or any derivative work
|
|
15
|
+
# thereof, to any third party, except pursuant to the Company's prior
|
|
16
|
+
# written consent.
|
|
17
|
+
# The Software is the confidential information of the Company.
|
|
18
|
+
# -------------------------------------------------------------------------------
|
|
19
|
+
import argparse
|
|
20
|
+
import math
|
|
21
|
+
import os
|
|
22
|
+
import csv
|
|
23
|
+
import json
|
|
24
|
+
import time
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from itertools import combinations
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
from ortools.sat.python import cp_model
|
|
30
|
+
from ortools.sat.python.cp_model import FIXED_SEARCH
|
|
31
|
+
from uni_model import GraphDefToUniModelConverter
|
|
32
|
+
from uni_model import UniModelToGraphDefConverter
|
|
33
|
+
from collections import defaultdict
|
|
34
|
+
from typing import List, Dict, Set
|
|
35
|
+
import json
|
|
36
|
+
from uni_model import UniModelPath
|
|
37
|
+
|
|
38
|
+
from conv_allocator.um_accessors import has_scratch, has_allocation, get_allocation, get_schedule, get_scratch_cut, get_cut, \
|
|
39
|
+
set_allocation
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(eq=False)
|
|
43
|
+
class Block:
|
|
44
|
+
layer_name: str
|
|
45
|
+
output_index: int
|
|
46
|
+
dynamic_memory: int
|
|
47
|
+
static_memory: int
|
|
48
|
+
offset: int
|
|
49
|
+
tensor_id: str
|
|
50
|
+
|
|
51
|
+
def __repr__(self):
|
|
52
|
+
return (f"Block({self.layer_name}, {self.output_index}, "
|
|
53
|
+
f"tensorId={self.tensor_id}, dynMem={self.dynamic_memory}, offset={self.offset})")
|
|
54
|
+
|
|
55
|
+
def __eq__(self, other):
|
|
56
|
+
return isinstance(other, Block) and (self.tensor_id == other.tensor_id)
|
|
57
|
+
|
|
58
|
+
def __hash__(self):
|
|
59
|
+
return hash((self.layer_name, self.output_index))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Filter out layers without output and allocations
|
|
63
|
+
def filter_relevant_nodes(nodes):
|
|
64
|
+
excluded_classes = {'UniLayerConst'}
|
|
65
|
+
|
|
66
|
+
def is_relevant(node):
|
|
67
|
+
class_name = node.__class__.__name__
|
|
68
|
+
if class_name in excluded_classes:
|
|
69
|
+
return False
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
return [node for node in nodes if is_relevant(node)]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def validate_allocation_data(nodes):
|
|
76
|
+
for node in nodes:
|
|
77
|
+
name = getattr(node, 'name', '<unknown>')
|
|
78
|
+
|
|
79
|
+
if not has_allocation(node):
|
|
80
|
+
raise ValueError(f"Node {name} missing 'allocations'")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
loaded_allocation = get_allocation(node)
|
|
84
|
+
except Exception:
|
|
85
|
+
raise ValueError(f"Node {name} has invalid 'allocations'")
|
|
86
|
+
|
|
87
|
+
if loaded_allocation.schedule is None:
|
|
88
|
+
raise ValueError(f"Node {name} missing 'schedule'")
|
|
89
|
+
|
|
90
|
+
if loaded_allocation.outputSizes is None:
|
|
91
|
+
raise ValueError(f"Node {name} missing 'outputSizes'")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def down_scale(out, scale):
|
|
95
|
+
return math.ceil(out.dynamicMemory/scale)
|
|
96
|
+
|
|
97
|
+
def parse_blocks_from_node(node, scale):
|
|
98
|
+
allocations_data = get_allocation(node)
|
|
99
|
+
blocks = [
|
|
100
|
+
Block(
|
|
101
|
+
out.layerName,
|
|
102
|
+
out.outputIndex,
|
|
103
|
+
down_scale(out, scale),
|
|
104
|
+
out.staticMemory,
|
|
105
|
+
out.offset,
|
|
106
|
+
out.tensorId
|
|
107
|
+
)
|
|
108
|
+
for out in allocations_data.outputSizes
|
|
109
|
+
]
|
|
110
|
+
return blocks
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create_fake_scratch_node(real_node):
|
|
114
|
+
class FakeScratchNode:
|
|
115
|
+
def __init__(self, source_node):
|
|
116
|
+
self.source_real_node = source_node
|
|
117
|
+
self.name = f"{source_node.name}_scratch"
|
|
118
|
+
|
|
119
|
+
return FakeScratchNode(real_node)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def build_graph_cuts(unimodel, scale):
|
|
123
|
+
graph = unimodel.uni_graphs[0]
|
|
124
|
+
filtered_nodes = filter_relevant_nodes(graph.layer_nodes)
|
|
125
|
+
validate_allocation_data(filtered_nodes)
|
|
126
|
+
|
|
127
|
+
# Step 1: Sort by schedule
|
|
128
|
+
sorted_nodes = sorted(filtered_nodes, key=get_schedule)
|
|
129
|
+
|
|
130
|
+
# Step 2: Parse blocks from sorted nodes
|
|
131
|
+
all_blocks_by_node = {}
|
|
132
|
+
blocks_map = {}
|
|
133
|
+
for node in sorted_nodes:
|
|
134
|
+
blocks = parse_blocks_from_node(node, scale)
|
|
135
|
+
all_blocks_by_node[node] = blocks
|
|
136
|
+
for block in blocks:
|
|
137
|
+
blocks_map[block] = node
|
|
138
|
+
|
|
139
|
+
# Step 3: Handle scratch layers, preserving order
|
|
140
|
+
scratch_fake_nodes = []
|
|
141
|
+
ordered_nodes_with_scratch = []
|
|
142
|
+
|
|
143
|
+
for node in sorted_nodes:
|
|
144
|
+
ordered_nodes_with_scratch.append(node)
|
|
145
|
+
if has_scratch(node):
|
|
146
|
+
scratch_node = create_fake_scratch_node(node)
|
|
147
|
+
scratch_fake_nodes.append(scratch_node)
|
|
148
|
+
ordered_nodes_with_scratch.append(scratch_node)
|
|
149
|
+
|
|
150
|
+
# Step 4: Use precomputed graph cuts from node metadata
|
|
151
|
+
tensor_id_to_block = {block.tensor_id: block for block in blocks_map}
|
|
152
|
+
graph_cuts = []
|
|
153
|
+
|
|
154
|
+
for node in ordered_nodes_with_scratch:
|
|
155
|
+
if hasattr(node, 'source_real_node'): # Scratch fake node
|
|
156
|
+
real_node = node.source_real_node
|
|
157
|
+
scratch_ids = get_scratch_cut(real_node)
|
|
158
|
+
block_set = {tensor_id_to_block[tid] for tid in scratch_ids}
|
|
159
|
+
else:
|
|
160
|
+
cut_ids = get_cut(node)
|
|
161
|
+
block_set = {tensor_id_to_block[tid] for tid in cut_ids}
|
|
162
|
+
|
|
163
|
+
graph_cuts.append(block_set)
|
|
164
|
+
|
|
165
|
+
return graph_cuts, blocks_map
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def solve_allocation(graph_cuts, blocks, time_limit):
|
|
169
|
+
"""
|
|
170
|
+
Solve the memory allocation problem using CP-SAT solver.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
graph_cuts: List of sets, each set contains block IDs that need to be in memory simultaneously
|
|
174
|
+
blocks: Dictionary mapping block ID to its size
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Dictionary mapping block ID to (start_time, end_time)
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
start_time = time.time()
|
|
181
|
+
|
|
182
|
+
# Estimate horizon (safe upper bound on end time)
|
|
183
|
+
horizon = int(sum(block.dynamic_memory for block in blocks) * 2)
|
|
184
|
+
|
|
185
|
+
model = cp_model.CpModel()
|
|
186
|
+
|
|
187
|
+
# Create variables for each block
|
|
188
|
+
block_dict = {}
|
|
189
|
+
for block in blocks.keys():
|
|
190
|
+
block_id = block.tensor_id
|
|
191
|
+
block_size = block.dynamic_memory
|
|
192
|
+
|
|
193
|
+
start_var = model.NewIntVar(0, horizon, f"start_{block_id}")
|
|
194
|
+
end_var = model.NewIntVar(0, horizon, f"end_{block_id}")
|
|
195
|
+
interval_var = model.NewIntervalVar(start_var, block_size, end_var, f"interval_{block_id}")
|
|
196
|
+
|
|
197
|
+
block_dict[block] = (start_var, end_var, interval_var)
|
|
198
|
+
|
|
199
|
+
# Solver setup
|
|
200
|
+
solver = cp_model.CpSolver()
|
|
201
|
+
solver.parameters.max_time_in_seconds = time_limit
|
|
202
|
+
solver.parameters.random_seed = 17
|
|
203
|
+
solver.parameters.randomize_search = False
|
|
204
|
+
solver.parameters.use_absl_random = False
|
|
205
|
+
solver.parameters.num_workers = 1
|
|
206
|
+
|
|
207
|
+
# fixed strategy - works with fixed_search
|
|
208
|
+
# activation_vars = [start for start, _, _ in block_dict.values()]
|
|
209
|
+
# model.AddDecisionStrategy(
|
|
210
|
+
# activation_vars,
|
|
211
|
+
# cp_model.CHOOSE_LOWEST_MIN,
|
|
212
|
+
# cp_model.SELECT_MIN_VALUE
|
|
213
|
+
# )
|
|
214
|
+
# solver.parameters.search_branching = cp_model.FIXED_SEARCH
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# Use AddNoOverlap per graph cut
|
|
218
|
+
for cut in graph_cuts:
|
|
219
|
+
cut_intervals = [block_dict[block][2] for block in cut]
|
|
220
|
+
model.AddNoOverlap(cut_intervals)
|
|
221
|
+
|
|
222
|
+
# Objective: minimize makespan (max end address)
|
|
223
|
+
obj_var = model.NewIntVar(0, horizon, "makespan")
|
|
224
|
+
model.AddMaxEquality(obj_var, [end for _, end, _ in block_dict.values()])
|
|
225
|
+
model.Minimize(obj_var)
|
|
226
|
+
|
|
227
|
+
# Solution callback
|
|
228
|
+
class SolutionCallback(cp_model.CpSolverSolutionCallback):
|
|
229
|
+
def __init__(self):
|
|
230
|
+
super().__init__()
|
|
231
|
+
self._solution_count = 0
|
|
232
|
+
self.latest_solution = None
|
|
233
|
+
|
|
234
|
+
def on_solution_callback(self):
|
|
235
|
+
self._solution_count += 1
|
|
236
|
+
self.latest_solution = {
|
|
237
|
+
block_id: (self.Value(start), self.Value(end))
|
|
238
|
+
for block_id, (start, end, _) in block_dict.items()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
callback = SolutionCallback()
|
|
242
|
+
|
|
243
|
+
status = solver.Solve(model, callback)
|
|
244
|
+
solve_time = time.time() - start_time
|
|
245
|
+
|
|
246
|
+
results = callback.latest_solution if callback.latest_solution else {
|
|
247
|
+
block_id: (solver.Value(start), solver.Value(end))
|
|
248
|
+
for block_id, (start, end, _) in block_dict.items()
|
|
249
|
+
} if status in [cp_model.OPTIMAL, cp_model.FEASIBLE] else {}
|
|
250
|
+
|
|
251
|
+
if status in [cp_model.OPTIMAL, cp_model.FEASIBLE]:
|
|
252
|
+
peak_memory = max(end for _, end in results.values())
|
|
253
|
+
print(f"Status: {solver.StatusName(status)}")
|
|
254
|
+
# print(f"Objective value: {solver.ObjectiveValue()}")
|
|
255
|
+
# print(f"Peak memory usage: {peak_memory}")
|
|
256
|
+
return results, solver, peak_memory, solve_time, True
|
|
257
|
+
else:
|
|
258
|
+
print(f"No solution found. Status: {solver.StatusName(status)}")
|
|
259
|
+
return results, solver, 0, solve_time, False
|
|
260
|
+
|
|
261
|
+
def main():
|
|
262
|
+
parser = argparse.ArgumentParser(description="Memory allocation solver.")
|
|
263
|
+
parser.add_argument("--input_path", required=True, help="Path to input file")
|
|
264
|
+
parser.add_argument("--output_path", required=True, help="Path to output file")
|
|
265
|
+
parser.add_argument("--time_limit", type=int, required=True, help="Time limit in seconds")
|
|
266
|
+
parser.add_argument("--scale", type=int, required=True, help="block size scale factor for address alignment.")
|
|
267
|
+
args = parser.parse_args()
|
|
268
|
+
|
|
269
|
+
scale = args.scale
|
|
270
|
+
uni_model_loaded = GraphDefToUniModelConverter.convert(args.input_path)
|
|
271
|
+
graph_cuts, blocks_map = build_graph_cuts(uni_model_loaded, scale)
|
|
272
|
+
results_scaled, allocation, max_address_scaled, solve_time, solved = solve_allocation(graph_cuts, blocks_map, args.time_limit)
|
|
273
|
+
max_address = max_address_scaled * scale
|
|
274
|
+
|
|
275
|
+
if solved:
|
|
276
|
+
layer_to_block_map = defaultdict(list)
|
|
277
|
+
for k, v in blocks_map.items():
|
|
278
|
+
layer_to_block_map[v].append(k)
|
|
279
|
+
|
|
280
|
+
block_id_to_offset = defaultdict(list)
|
|
281
|
+
for block in results_scaled:
|
|
282
|
+
offset = results_scaled[block][0] * scale
|
|
283
|
+
block_id_to_offset[block.tensor_id] = offset
|
|
284
|
+
|
|
285
|
+
for node in uni_model_loaded.uni_graphs[0].layer_nodes:
|
|
286
|
+
if has_allocation(node):
|
|
287
|
+
# Load allocation object from accessor
|
|
288
|
+
alloc_data = get_allocation(node)
|
|
289
|
+
|
|
290
|
+
# Update the outputSizes offsets inside AllocationDataPerLayer
|
|
291
|
+
for output_alloc in alloc_data.outputSizes:
|
|
292
|
+
output_alloc.offset = block_id_to_offset[output_alloc.tensorId]
|
|
293
|
+
|
|
294
|
+
# Write updated allocation object back to node
|
|
295
|
+
set_allocation(node, alloc_data)
|
|
296
|
+
|
|
297
|
+
# debug print addresses
|
|
298
|
+
# for node in uni_model_loaded.uni_graphs[0].layer_nodes:
|
|
299
|
+
# if has_allocation(node):
|
|
300
|
+
# alloc_data = get_allocation(node)
|
|
301
|
+
# for output_alloc in alloc_data.outputSizes:
|
|
302
|
+
# output_alloc.offset = block_id_to_offset[output_alloc.tensorId]
|
|
303
|
+
# print(f"tid: {output_alloc.tensorId}, offset: {output_alloc.offset}")
|
|
304
|
+
|
|
305
|
+
print("Solution found")
|
|
306
|
+
print(f"max_address: {max_address}")
|
|
307
|
+
print(f"solve_time: {solve_time}")
|
|
308
|
+
else:
|
|
309
|
+
print("No solution found")
|
|
310
|
+
|
|
311
|
+
path = Path(args.output_path)
|
|
312
|
+
|
|
313
|
+
directory = str(path.parent)
|
|
314
|
+
ext = ".um.pb"
|
|
315
|
+
filename_wo_ext = os.path.basename(path)
|
|
316
|
+
if filename_wo_ext.endswith(ext):
|
|
317
|
+
filename_wo_ext = filename_wo_ext[:-len(ext)]
|
|
318
|
+
|
|
319
|
+
uniPath = UniModelPath(directory, filename_wo_ext)
|
|
320
|
+
UniModelToGraphDefConverter.convert(uni_model_loaded, uniPath)
|
|
321
|
+
|
|
322
|
+
if __name__ == "__main__":
|
|
323
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# (c) Copyright 2025 Sony Semiconductor Israel, Ltd. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This software, in source or object form (the "Software"), is the
|
|
5
|
+
# property of Sony Semiconductor Israel Ltd. (the "Company") and/or its
|
|
6
|
+
# licensors, which have all right, title and interest therein, You
|
|
7
|
+
# may use the Software only in accordance with the terms of written
|
|
8
|
+
# license agreement between you and the Company (the "License").
|
|
9
|
+
# Except as expressly stated in the License, the Company grants no
|
|
10
|
+
# licenses by implication, estoppel, or otherwise. If you are not
|
|
11
|
+
# aware of or do not agree to the License terms, you may not use,
|
|
12
|
+
# copy or modify the Software. You may use the source code of the
|
|
13
|
+
# Software only for your internal purposes and may not distribute the
|
|
14
|
+
# source code of the Software, any part thereof, or any derivative work
|
|
15
|
+
# thereof, to any third party, except pursuant to the Company's prior
|
|
16
|
+
# written consent.
|
|
17
|
+
# The Software is the confidential information of the Company.
|
|
18
|
+
# -------------------------------------------------------------------------------
|
|
19
|
+
import sys
|
|
20
|
+
import argparse
|
|
21
|
+
from ortools.linear_solver import pywraplp
|
|
22
|
+
|
|
23
|
+
def parse_args(args=None):
|
|
24
|
+
parser = argparse.ArgumentParser(description='Allocation optimizer')
|
|
25
|
+
parser.add_argument('--source', type=str, help='Source filename', default='')
|
|
26
|
+
parser.add_argument('--target', type=str, help='Target filename', default='')
|
|
27
|
+
parser.add_argument('--time', type=int, help='Allowed time in seconds', default=0)
|
|
28
|
+
|
|
29
|
+
return parser.parse_args(args)
|
|
30
|
+
|
|
31
|
+
def hello_world(source_filename, target_filename, allowed_time):
|
|
32
|
+
print(f"Hello from conv-allocator!")
|
|
33
|
+
print(f"Source: {source_filename}")
|
|
34
|
+
print(f"Target: {target_filename}")
|
|
35
|
+
print(f"Allowed time: {allowed_time} seconds")
|
|
36
|
+
|
|
37
|
+
solver = pywraplp.Solver.CreateSolver('GLOP')
|
|
38
|
+
x = solver.NumVar(0, 10, 'x')
|
|
39
|
+
y = solver.NumVar(0, 10, 'y')
|
|
40
|
+
solver.Add(x + y <= 20)
|
|
41
|
+
solver.Maximize(x + y)
|
|
42
|
+
status = solver.Solve()
|
|
43
|
+
if status == pywraplp.Solver.OPTIMAL:
|
|
44
|
+
print(f"x = {x.solution_value()}, y = {y.solution_value()}")
|
|
45
|
+
else:
|
|
46
|
+
print("No solution found")
|
|
47
|
+
|
|
48
|
+
def main(args=None):
|
|
49
|
+
if args is None:
|
|
50
|
+
# Use sys.argv if no args provided
|
|
51
|
+
args = sys.argv[1:]
|
|
52
|
+
|
|
53
|
+
parsed_args = parse_args(args)
|
|
54
|
+
hello_world(parsed_args.source, parsed_args.target, parsed_args.time)
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -------------------------------------------------------------------------------
|
|
2
|
+
# (c) Copyright 2025 Sony Semiconductor Israel, Ltd. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This software, in source or object form (the "Software"), is the
|
|
5
|
+
# property of Sony Semiconductor Israel Ltd. (the "Company") and/or its
|
|
6
|
+
# licensors, which have all right, title and interest therein, You
|
|
7
|
+
# may use the Software only in accordance with the terms of written
|
|
8
|
+
# license agreement between you and the Company (the "License").
|
|
9
|
+
# Except as expressly stated in the License, the Company grants no
|
|
10
|
+
# licenses by implication, estoppel, or otherwise. If you are not
|
|
11
|
+
# aware of or do not agree to the License terms, you may not use,
|
|
12
|
+
# copy or modify the Software. You may use the source code of the
|
|
13
|
+
# Software only for your internal purposes and may not distribute the
|
|
14
|
+
# source code of the Software, any part thereof, or any derivative work
|
|
15
|
+
# thereof, to any third party, except pursuant to the Company's prior
|
|
16
|
+
# written consent.
|
|
17
|
+
# The Software is the confidential information of the Company.
|
|
18
|
+
# -------------------------------------------------------------------------------
|
|
19
|
+
import json
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from json import JSONEncoder, dumps
|
|
22
|
+
from typing import Optional, List
|
|
23
|
+
from uni_model import UniModel, UniGraph, UniLayer
|
|
24
|
+
|
|
25
|
+
ALLOCATIONS = 'allocations'
|
|
26
|
+
HAS_SCRATCH = 'hasScratch'
|
|
27
|
+
OUTPUT_SIZES = 'outputSizes'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def has_scratch(layer: UniLayer):
|
|
31
|
+
return layer.extended_attr.get(HAS_SCRATCH, False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def has_allocation(layer: UniLayer):
|
|
35
|
+
return layer.extended_attr.get(ALLOCATIONS, False)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_allocation(layer: UniLayer):
|
|
39
|
+
return AllocationDataPerLayer.decode_from_json(layer.extended_attr.get(ALLOCATIONS))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_schedule(node: UniLayer):
|
|
45
|
+
if has_allocation(node):
|
|
46
|
+
return get_allocation(node).schedule
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError(f"Node {node.name} missing 'allocation'")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_scratch_cut(node: UniLayer):
|
|
52
|
+
if has_allocation(node):
|
|
53
|
+
return get_allocation(node).scratchCutTensorIds
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(f"Node {node.name} missing 'allocation'")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_cut(node: UniLayer):
|
|
59
|
+
if has_allocation(node):
|
|
60
|
+
return get_allocation(node).cutTensorIds
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(f"Node {node.name} missing 'allocation'")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class OutputAllocation:
|
|
67
|
+
tensorId: str
|
|
68
|
+
layerName: str
|
|
69
|
+
outputIndex: int
|
|
70
|
+
dynamicMemory: int
|
|
71
|
+
staticMemory: int
|
|
72
|
+
offset: Optional[int] = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class AllocationDataPerLayer:
|
|
77
|
+
schedule: int
|
|
78
|
+
dynamicMemory: int
|
|
79
|
+
cutTensorIds: list[str] = field(default_factory=list)
|
|
80
|
+
scratchCutTensorIds: list[str] = field(default_factory=list)
|
|
81
|
+
outputSizes: list[OutputAllocation] = field(default_factory=list)
|
|
82
|
+
|
|
83
|
+
def encode_to_json(self) -> str:
|
|
84
|
+
d = self.__dict__.copy()
|
|
85
|
+
d[OUTPUT_SIZES] = [o.__dict__ for o in d[OUTPUT_SIZES]]
|
|
86
|
+
return json.dumps(d)
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def decode_from_json(j: str) -> 'AllocationDataPerLayer':
|
|
90
|
+
d = json.loads(j)
|
|
91
|
+
d[OUTPUT_SIZES] = [OutputAllocation(**o) for o in d[OUTPUT_SIZES]]
|
|
92
|
+
return AllocationDataPerLayer(**d)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def set_allocation(layer: UniLayer, alloc_data: AllocationDataPerLayer):
|
|
96
|
+
layer.extended_attr[ALLOCATIONS] = alloc_data.encode_to_json()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
a = AllocationDataPerLayer(
|
|
101
|
+
schedule=4,
|
|
102
|
+
dynamicMemory=9,
|
|
103
|
+
cutTensorIds=["t1", "t2"],
|
|
104
|
+
scratchCutTensorIds=["t2"],
|
|
105
|
+
outputSizes=[
|
|
106
|
+
OutputAllocation(tensorId="t1", layerName="layerX", outputIndex=0, dynamicMemory=10, staticMemory=5)
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
encoded = a.encode_to_json()
|
|
110
|
+
print("Serialized:", encoded)
|
|
111
|
+
|
|
112
|
+
decoded = AllocationDataPerLayer.decode_from_json(encoded)
|
|
113
|
+
print("Deserialized:", decoded)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
conv_allocator/__init__.py,sha256=phGsoy1jdNsCmzQFKZEWB_Thbw633GKNXRHR4TKs_0w,1208
|
|
2
|
+
conv_allocator/allocator.py,sha256=aXp_obbxqtrK3wq57N0WmUY2wUXcn2HdegOuxro_K20,11710
|
|
3
|
+
conv_allocator/allocator_test.py,sha256=sCWtHJKG2kH2f_ppRdNALwhgucK-mWEJyM-JfnGkN3U,2524
|
|
4
|
+
conv_allocator/um_accessors.py,sha256=-6AIZ3x53ILeLBZCsIMI2b-ywslwWGd-QWK7RCy7n9c,3832
|
|
5
|
+
conv_allocator-3.17.1.dist-info/METADATA,sha256=hTlJGQXJS3iwodf0njPo_fbtWENquKMPNaDWjzkE0lk,159
|
|
6
|
+
conv_allocator-3.17.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
7
|
+
conv_allocator-3.17.1.dist-info/entry_points.txt,sha256=hLQ1x1lLRtbJWuJG8CtSpOdaN28hbYAVd_07szV-cKU,64
|
|
8
|
+
conv_allocator-3.17.1.dist-info/top_level.txt,sha256=JWue8wV0JXEagm9a0qky3c1sKcImcUrQoJ_JskNFiqQ,15
|
|
9
|
+
conv_allocator-3.17.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
conv_allocator
|