Skip to content

Commit 8fef233

Browse files
authored
Use input size limits for constant folding (#1903)
Add input size limits for constant folding. Helps avoid excessive time in optimizer in some edge cases. (The edge cases, where we have non-trivial ops applied to large tensors, are not relevant for the exporter itself. They may be of potential interest for optimization in other settings, but that can be done by user taking explicit steps.) Still to be done: how do we specify these values from the benchmarking code? For now, the default values will be quite useful, but experimenting with these values from the benchmarking code will need a way to control these option values.
1 parent ed28222 commit 8fef233

File tree

2 files changed

+58
-8
lines changed

2 files changed

+58
-8
lines changed

onnxscript/optimizer/__init__.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,46 @@ def optimize(
111111
return model
112112

113113

114+
_DEFAULT_CONSTANT_FOLD_INPUT_SIZE_LIMIT = (
115+
_constant_folding._DEFAULT_CONSTANT_FOLD_INPUT_SIZE_LIMIT
116+
)
117+
118+
_DEFAULT_CONSTANT_FOLD_OUTPUT_SIZE_LIMIT = (
119+
_constant_folding._DEFAULT_CONSTANT_FOLD_OUTPUT_SIZE_LIMIT
120+
)
121+
122+
114123
def optimize_ir(
115124
model: ir.Model,
116125
num_iterations: int = 2,
117126
*,
118127
onnx_shape_inference: bool = True,
119128
stop_if_no_change: bool = True,
129+
input_size_limit: int = _DEFAULT_CONSTANT_FOLD_INPUT_SIZE_LIMIT,
130+
output_size_limit: int = _DEFAULT_CONSTANT_FOLD_OUTPUT_SIZE_LIMIT,
120131
) -> None:
132+
"""Optimizes a model.
133+
134+
Args:
135+
model: The model to be optimized.
136+
num_iterations: Number of times the optimization loop is repeated.
137+
onnx_shape_inference: Applies node-level shape-inference as part of optimization
138+
input_size_limit: Will not apply constant folding to ops with any input of size
139+
greater than this. Does not apply to special ops like Shape() and Size().
140+
output_size_limit: Will not rewrite any foldable-op into a Constant op if the size
141+
of the output tensor is greater than this.
142+
stop_if_no_change: Not supported currently (has no effect). Meant to stop the
143+
outer optimization loop if no change is detected in one iteration.
144+
"""
121145
del stop_if_no_change # Looks like rewriter doesn't support this yet.
122146
_inliner.inline(model)
123147
for _ in range(num_iterations):
124-
_constant_folding.fold_constants(model, onnx_shape_inference=onnx_shape_inference)
148+
_constant_folding.fold_constants(
149+
model,
150+
onnx_shape_inference=onnx_shape_inference,
151+
input_size_limit=input_size_limit,
152+
output_size_limit=output_size_limit,
153+
)
125154
rewriter.rewrite(model, pattern_rewrite_rules=_DEFAULT_REWRITE_RULES)
126155
remove_unused_nodes(model)
127156

onnxscript/optimizer/_constant_folding.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ def is_constant_op(node: ir.Node) -> bool:
4343
)
4444

4545

46-
_DEFAULT_CONSTANT_FOLD_SIZE_LIMIT = constant_folding._DEFAULT_CONSTANT_FOLD_SIZE_LIMIT
46+
_DEFAULT_CONSTANT_FOLD_INPUT_SIZE_LIMIT = 1024
47+
48+
_DEFAULT_CONSTANT_FOLD_OUTPUT_SIZE_LIMIT = constant_folding._DEFAULT_CONSTANT_FOLD_SIZE_LIMIT
4749

4850
logger = logging.getLogger(__name__)
4951

@@ -550,11 +552,16 @@ class ConstantFolder:
550552

551553
def __init__(
552554
self,
555+
*,
553556
external_data_folder: str,
554-
do_shape_inference: bool,
557+
shape_inference: bool,
558+
input_size_limit: int,
559+
output_size_limit: int,
555560
) -> None:
556561
self._external_data_folder = external_data_folder
557-
self._do_shape_inference = do_shape_inference
562+
self._shape_inference = shape_inference
563+
self._input_size_limit = input_size_limit
564+
self._output_size_limit = output_size_limit
558565
self._init()
559566

560567
def _init(self) -> None:
@@ -632,7 +639,7 @@ def new_constant(self, irvalue: ir.Value, value):
632639

633640
irvalue.const_value = _convenience.tensor(value)
634641

635-
if value.nbytes > _DEFAULT_CONSTANT_FOLD_SIZE_LIMIT:
642+
if value.nbytes > self._output_size_limit:
636643
logger.info(
637644
"Skip storing constant folded nvalue %s due to large size %s.",
638645
irvalue.name,
@@ -667,7 +674,7 @@ def process_node(self, node: ir.Node):
667674
# TODO(rama): consider merging type/other info from both values
668675

669676
# Do incremental shape inference
670-
if self._do_shape_inference and not is_control_flow_op(node):
677+
if self._shape_inference and not is_control_flow_op(node):
671678
self._do_inference(node)
672679

673680
if node.domain not in self.opset_imports:
@@ -696,6 +703,16 @@ def process_node(self, node: ir.Node):
696703
if any(x is None for x in input_values):
697704
return None
698705

706+
if any(input.size > self._input_size_limit for input in input_values): # type: ignore[union-attr]
707+
if logger.isEnabledFor(logging.DEBUG):
708+
input_sizes = [input.size for input in input_values] # type: ignore[union-attr]
709+
logger.debug(
710+
"Skipping constant folding for op %s due to large input size: %s",
711+
node.op_type,
712+
input_sizes,
713+
)
714+
return None
715+
699716
# Filter out bfloat16 cases?
700717
def convert(av):
701718
if av.type == ir.AttributeType.TENSOR:
@@ -770,14 +787,18 @@ def fold_constants(
770787
external_data_folder: str = "",
771788
*,
772789
onnx_shape_inference: bool = False,
790+
input_size_limit: int = _DEFAULT_CONSTANT_FOLD_INPUT_SIZE_LIMIT,
791+
output_size_limit: int = _DEFAULT_CONSTANT_FOLD_OUTPUT_SIZE_LIMIT,
773792
) -> bool:
774793
"""
775794
Applies constant folding optimization to the model.
776795
Returns true iff the model was modified.
777796
"""
778797
folder = ConstantFolder(
779-
external_data_folder,
780-
onnx_shape_inference,
798+
external_data_folder=external_data_folder,
799+
shape_inference=onnx_shape_inference,
800+
input_size_limit=input_size_limit,
801+
output_size_limit=output_size_limit,
781802
)
782803
folder.visit_model(model)
783804
for op in folder.counts:

0 commit comments

Comments
 (0)