Skip to content

Commit 0953f2f

Browse files
committed
Update ONNX-GraphSurgeon to v0.3.9
Signed-off-by: Rajeev Rao <rajeevrao@nvidia.com>
1 parent 1075538 commit 0953f2f

4 files changed

Lines changed: 70 additions & 7 deletions

File tree

tools/onnx-graphsurgeon/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
Dates are in YYYY-MM-DD format.
44

55

6+
## v0.3.9 (2021-04-20)
7+
### Changed
8+
- `fold_constants()` will no longer store values for foldable tensors whose outputs are all foldable.
9+
For example, while folding a constant subgraph like `A (constant) -> B -> C`, previously, `B` values
10+
would be computed in addition to `C`. With these changes, only `C` values are computed and stored.
11+
This can reduce memory usage significantly.
12+
13+
14+
## v0.3.8 (2021-04-15)
15+
### Fixed
16+
- Fixed a bug where `copy()` would not work with subgraphs that included tensors with the same
17+
names as outer graph tensors unless a `tensor_map` was provided.
18+
19+
620
## v0.3.7 (2021-03-31)
721
### Added
822
- `fold_constants()` can now fold `Shape -> Gather` patterns even when the entire shape may not be known.

tools/onnx-graphsurgeon/onnx_graphsurgeon/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
from onnx_graphsurgeon.ir.tensor import Constant, Tensor, Variable
66
from onnx_graphsurgeon.util.exception import OnnxGraphSurgeonException
77

8-
__version__ = "0.3.7"
8+
__version__ = "0.3.9"

tools/onnx-graphsurgeon/onnx_graphsurgeon/ir/graph.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ def _get_node_id(self, node):
169169
def _local_tensors(self):
170170
local_tensors = {t.name: t for node in self.nodes for t in node.outputs if not t.is_empty()}
171171
local_tensors.update({t.name: t for t in self.inputs})
172+
local_tensors.update({t.name: t for t in self.tensors().values() if isinstance(t, Constant)})
172173
return local_tensors
173174

174175

@@ -279,7 +280,7 @@ def cleanup_subgraphs():
279280
if recurse_subgraphs:
280281
cleanup_subgraphs()
281282

282-
G_LOGGER.debug("Cleaning up {:}".format(self.name))
283+
G_LOGGER.verbose("Cleaning up {:}".format(self.name))
283284

284285
with self.node_ids():
285286
# Graph input producers must be removed first so used_node_ids is correct.
@@ -658,7 +659,17 @@ def get_out_node_ids():
658659

659660

660661
# Next, evaluate the foldable variables with ONNX-Runtime
661-
graph_clone.outputs = [t for t in graph_constants.values() if not isinstance(t, Constant)]
662+
663+
# Only evaluate foldable values that have non-foldable outputs or are graph outputs.
664+
# Otherwise, if all the outputs are foldable, then we can just evaluate the outputs directly.
665+
def should_eval_foldable(tensor):
666+
non_const = not isinstance(tensor, Constant)
667+
is_graph_output = not tensor.outputs
668+
has_non_foldable_outputs = any(out.name not in graph_constants for out in tensor.outputs)
669+
return non_const and (is_graph_output or has_non_foldable_outputs)
670+
671+
graph_clone.outputs = [t for t in graph_constants.values() if should_eval_foldable(t)]
672+
G_LOGGER.debug("Folding tensors: {:}".format(graph_clone.outputs))
662673
graph_clone.cleanup(remove_unused_graph_inputs=True)
663674

664675
# Using ._values avoids a deep copy of the values.
@@ -789,9 +800,15 @@ def copy(self, tensor_map: "OrderedDict[str, Tensor]"=None):
789800
# First, reconstruct each tensor in the graph, but with no inputs or outputs
790801
tensor_map = copy.copy(misc.default_value(tensor_map, {}))
791802

792-
local_tensors = self.tensors()
793-
local_tensor_copies = {name: tensor.copy() for name, tensor in local_tensors.items()}
803+
local_tensor_copies = {}
804+
# When we're cloning a subgraph by itself, we need to use `tensors()` to get all
805+
# required tensors - even those produced by outer graphs.
806+
local_tensor_copies.update({n: t.copy() for n, t in self.tensors().items()})
807+
# However, we should prioritize copies already made by the outer graph.
794808
local_tensor_copies.update(tensor_map)
809+
# And locally produced tensors should take precedence over everything else.
810+
local_tensor_copies.update({n: t.copy() for n, t in self._local_tensors().items()})
811+
795812

796813
def get_tensor(name):
797814
if not name:

tools/onnx-graphsurgeon/tests/ir/test_graph.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,38 @@ def test_copy_with_subgraph(self, nested_graph):
612612
assert len(subgraph.nodes) == 2
613613

614614

615+
# If the subgraph has a tensor with the same name as the outer graph,
616+
# the subgraph copy should include a copy of the subgraph tensor, not the outer
617+
# graph tensor.
618+
def test_copy_with_subgraph_dup_tensors(self):
619+
inp = Variable("input", dtype=np.float32, shape=(4, 5))
620+
graph = Graph(inputs=[inp])
621+
622+
# We'll use shape to distinguish inner/outer tensor
623+
subgraph_inp = Variable("input", dtype=np.float32, shape=(1, 2))
624+
subgraph = Graph(inputs=[subgraph_inp])
625+
626+
graph.outputs = [graph.nested(inp, subgraph)]
627+
628+
graph_copy = graph.copy()
629+
assert graph_copy.nodes[0].attrs["body"].inputs[0].shape == (1, 2)
630+
631+
632+
def test_copy_with_subgraph_dup_const_tensors(self):
633+
inp = Constant("input", values=np.ones(dtype=np.float32, shape=(4, 5)))
634+
graph = Graph()
635+
636+
# We'll use shape to distinguish inner/outer tensor
637+
subgraph_inp = Constant("input", values=np.ones(dtype=np.float32, shape=(1, 2)))
638+
subgraph = Graph()
639+
subgraph.outputs = [subgraph.identity(subgraph_inp)]
640+
641+
graph.outputs = [graph.nested(inp, subgraph)]
642+
643+
graph_copy = graph.copy()
644+
assert graph_copy.nodes[0].attrs["body"].nodes[0].inputs[0].shape == (1, 2)
645+
646+
615647
@pytest.fixture
616648
def simple_foldable():
617649
# Graph:
@@ -783,8 +815,8 @@ def test_shape_of_constant_node(self):
783815

784816
# Cannot fold shape nodes if they have dynamically shaped inputs.
785817
def test_shape_of_variable_tensor_dynamic_shape(self):
786-
graph = Graph()
787818
var = Variable("var", dtype=np.float32, shape=("", -1, 0, 4))
819+
graph = Graph(inputs=[var])
788820
graph.outputs = [graph.shape(var)]
789821

790822
graph.fold_constants().cleanup()
@@ -795,8 +827,8 @@ def test_shape_of_variable_tensor_dynamic_shape(self):
795827

796828

797829
def test_shape_of_variable_tensor_static_shape(self):
798-
graph = Graph()
799830
var = Variable("var", dtype=np.float32, shape=(1, 3, 4))
831+
graph = Graph(inputs=[var])
800832
graph.inputs = [var]
801833
graph.outputs = [graph.shape(var)]
802834

0 commit comments

Comments
 (0)