onnxscript 0.1.0__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.
- onnxscript/__init__.py +131 -0
- onnxscript/_framework_apis/__init__.py +3 -0
- onnxscript/_framework_apis/torch_2_5.py +117 -0
- onnxscript/_framework_apis/torch_2_6.py +45 -0
- onnxscript/_internal/__init__.py +0 -0
- onnxscript/_internal/analysis.py +229 -0
- onnxscript/_internal/ast_utils.py +64 -0
- onnxscript/_internal/autocast.py +250 -0
- onnxscript/_internal/deprecation.py +78 -0
- onnxscript/_internal/param_manipulation.py +148 -0
- onnxscript/_internal/runtime_typing.py +43 -0
- onnxscript/_internal/utils.py +99 -0
- onnxscript/_internal/version_utils.py +118 -0
- onnxscript/_legacy_ir/__init__.py +341 -0
- onnxscript/_legacy_ir/visitor.py +937 -0
- onnxscript/_thirdparty/asciichartpy.py +313 -0
- onnxscript/backend/__init__.py +2 -0
- onnxscript/backend/onnx_backend.py +303 -0
- onnxscript/backend/onnx_export.py +885 -0
- onnxscript/converter.py +1470 -0
- onnxscript/evaluator.py +619 -0
- onnxscript/function_libs/tools/torch_lib/deduce_type_constraints.py +403 -0
- onnxscript/function_libs/tools/torch_lib/generate_aten_signatures.py +333 -0
- onnxscript/function_libs/tools/torch_lib/generate_prims_signatures.py +331 -0
- onnxscript/function_libs/torch_lib/__init__.py +12 -0
- onnxscript/function_libs/torch_lib/_constants.py +5 -0
- onnxscript/function_libs/torch_lib/_flags.py +58 -0
- onnxscript/function_libs/torch_lib/graph_building/__init__.py +56 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_ir.py +723 -0
- onnxscript/function_libs/torch_lib/graph_building/_graph_building_torch.py +1125 -0
- onnxscript/function_libs/torch_lib/ops/__init__.py +27 -0
- onnxscript/function_libs/torch_lib/ops/common.py +80 -0
- onnxscript/function_libs/torch_lib/ops/core.py +8935 -0
- onnxscript/function_libs/torch_lib/ops/fft.py +385 -0
- onnxscript/function_libs/torch_lib/ops/linalg.py +399 -0
- onnxscript/function_libs/torch_lib/ops/nested.py +25 -0
- onnxscript/function_libs/torch_lib/ops/nn.py +2713 -0
- onnxscript/function_libs/torch_lib/ops/prims.py +850 -0
- onnxscript/function_libs/torch_lib/ops/quantized_decomposed.py +63 -0
- onnxscript/function_libs/torch_lib/ops/sparse.py +23 -0
- onnxscript/function_libs/torch_lib/ops/special.py +387 -0
- onnxscript/function_libs/torch_lib/ops/vision.py +25 -0
- onnxscript/function_libs/torch_lib/registration.py +151 -0
- onnxscript/function_libs/torch_lib/tensor_typing.py +74 -0
- onnxscript/ir/__init__.py +153 -0
- onnxscript/ir/_convenience.py +447 -0
- onnxscript/ir/_core.py +3119 -0
- onnxscript/ir/_display.py +49 -0
- onnxscript/ir/_enums.py +163 -0
- onnxscript/ir/_graph_comparison.py +23 -0
- onnxscript/ir/_io.py +97 -0
- onnxscript/ir/_linked_list.py +276 -0
- onnxscript/ir/_metadata.py +44 -0
- onnxscript/ir/_name_authority.py +72 -0
- onnxscript/ir/_polyfill.py +25 -0
- onnxscript/ir/_protocols.py +598 -0
- onnxscript/ir/_schemas.py +548 -0
- onnxscript/ir/_tape.py +120 -0
- onnxscript/ir/_type_casting.py +106 -0
- onnxscript/ir/convenience.py +32 -0
- onnxscript/ir/external_data.py +396 -0
- onnxscript/ir/passes/__init__.py +33 -0
- onnxscript/ir/passes/_pass_infra.py +172 -0
- onnxscript/ir/serde.py +1620 -0
- onnxscript/ir/tensor_adapters.py +122 -0
- onnxscript/ir/traversal.py +82 -0
- onnxscript/irbuilder.py +542 -0
- onnxscript/main.py +167 -0
- onnxscript/onnx_opset/__init__.py +232 -0
- onnxscript/onnx_opset/_impl/opset1.py +4100 -0
- onnxscript/onnx_opset/_impl/opset10.py +1227 -0
- onnxscript/onnx_opset/_impl/opset11.py +4013 -0
- onnxscript/onnx_opset/_impl/opset12.py +1078 -0
- onnxscript/onnx_opset/_impl/opset13.py +3924 -0
- onnxscript/onnx_opset/_impl/opset14.py +999 -0
- onnxscript/onnx_opset/_impl/opset15.py +604 -0
- onnxscript/onnx_opset/_impl/opset16.py +1255 -0
- onnxscript/onnx_opset/_impl/opset17.py +561 -0
- onnxscript/onnx_opset/_impl/opset18.py +1803 -0
- onnxscript/onnx_opset/_impl/opset19.py +1942 -0
- onnxscript/onnx_opset/_impl/opset2.py +218 -0
- onnxscript/onnx_opset/_impl/opset20.py +675 -0
- onnxscript/onnx_opset/_impl/opset21.py +1976 -0
- onnxscript/onnx_opset/_impl/opset22.py +2588 -0
- onnxscript/onnx_opset/_impl/opset3.py +199 -0
- onnxscript/onnx_opset/_impl/opset4.py +77 -0
- onnxscript/onnx_opset/_impl/opset5.py +84 -0
- onnxscript/onnx_opset/_impl/opset6.py +944 -0
- onnxscript/onnx_opset/_impl/opset7.py +1243 -0
- onnxscript/onnx_opset/_impl/opset8.py +444 -0
- onnxscript/onnx_opset/_impl/opset9.py +1485 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml1.py +974 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml2.py +112 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml3.py +308 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml4.py +129 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_ml5.py +158 -0
- onnxscript/onnx_opset/_impl/opset_ai_onnx_preview_training1.py +577 -0
- onnxscript/onnx_types.py +229 -0
- onnxscript/optimizer/__init__.py +39 -0
- onnxscript/optimizer/_constant_folding.py +1083 -0
- onnxscript/optimizer/_inliner.py +312 -0
- onnxscript/optimizer/_legacy/_optimizer.py +98 -0
- onnxscript/optimizer/_legacy/_remove_unused_proto.py +144 -0
- onnxscript/optimizer/_legacy/_simple_function_folding.py +243 -0
- onnxscript/optimizer/_legacy/constant_folding.py +293 -0
- onnxscript/optimizer/_legacy/evaluator.py +439 -0
- onnxscript/optimizer/_optimizer.py +61 -0
- onnxscript/optimizer/_remove_unused.py +106 -0
- onnxscript/optimizer/_remove_unused_function.py +72 -0
- onnxscript/py.typed +1 -0
- onnxscript/rewriter/__init__.py +56 -0
- onnxscript/rewriter/_ir_utils.py +111 -0
- onnxscript/rewriter/broadcast_to_matmul.py +180 -0
- onnxscript/rewriter/cast_constant_of_shape.py +50 -0
- onnxscript/rewriter/collapse_slices.py +141 -0
- onnxscript/rewriter/erfgelu.py +27 -0
- onnxscript/rewriter/function_rule.py +232 -0
- onnxscript/rewriter/gemm_to_matmul_add.py +21 -0
- onnxscript/rewriter/generic_pattern.py +700 -0
- onnxscript/rewriter/llama_rule_sets.py +287 -0
- onnxscript/rewriter/no_op.py +56 -0
- onnxscript/rewriter/onnxruntime/__init__.py +50 -0
- onnxscript/rewriter/onnxruntime/bfloat16_utils/bfloat16_converter.py +99 -0
- onnxscript/rewriter/onnxruntime/fused_matmul_rule_sets.py +177 -0
- onnxscript/rewriter/onnxruntime/group_normalization_merge_silu.py +64 -0
- onnxscript/rewriter/onnxruntime/instance_to_group_normalization.py +155 -0
- onnxscript/rewriter/onnxruntime/softmax.py +63 -0
- onnxscript/rewriter/onnxruntime/transformers/__init__.py +21 -0
- onnxscript/rewriter/onnxruntime/transformers/biassplitgelu.py +31 -0
- onnxscript/rewriter/onnxruntime/transformers/fastgelu.py +29 -0
- onnxscript/rewriter/onnxruntime/transformers/layernorm.py +47 -0
- onnxscript/rewriter/onnxruntime/transformers/multihead_attention.py +715 -0
- onnxscript/rewriter/ort_fusions/__init__.py +9 -0
- onnxscript/rewriter/ort_fusions/_core.py +28 -0
- onnxscript/rewriter/ort_fusions/_smollm_1.py +253 -0
- onnxscript/rewriter/ort_fusions/_smollm_2.py +467 -0
- onnxscript/rewriter/ort_fusions/_test_models.py +122 -0
- onnxscript/rewriter/ort_fusions/_test_utils.py +42 -0
- onnxscript/rewriter/ort_fusions/cos_sin_cache.py +154 -0
- onnxscript/rewriter/ort_fusions/gqa.py +156 -0
- onnxscript/rewriter/ort_fusions/mha.py +198 -0
- onnxscript/rewriter/ort_fusions/rms_normalization.py +95 -0
- onnxscript/rewriter/ort_fusions/rotary_embedding.py +64 -0
- onnxscript/rewriter/ort_fusions/sdpa.py +75 -0
- onnxscript/rewriter/ort_fusions/skip_normalization.py +46 -0
- onnxscript/rewriter/pattern.py +1714 -0
- onnxscript/rewriter/testing.py +77 -0
- onnxscript/sourceinfo.py +59 -0
- onnxscript/tensor.py +227 -0
- onnxscript/testing/__init__.py +482 -0
- onnxscript/tools/__init__.py +4 -0
- onnxscript/tools/benchmark/__init__.py +23 -0
- onnxscript/tools/benchmark/benchmark_helpers.py +783 -0
- onnxscript/tools/benchmark/benchmark_run.py +140 -0
- onnxscript/tools/benchmark/export_model.py +207 -0
- onnxscript/tools/benchmark/export_model_batch.py +146 -0
- onnxscript/tools/memory_peak.py +244 -0
- onnxscript/tools/training_helper.py +50 -0
- onnxscript/tools/transformers_models/__init__.py +190 -0
- onnxscript/tools/transformers_models/llama.py +168 -0
- onnxscript/tools/transformers_models/mistral.py +238 -0
- onnxscript/tools/transformers_models/phi.py +248 -0
- onnxscript/tools/transformers_models/phi3.py +259 -0
- onnxscript/type_annotation.py +281 -0
- onnxscript/utils/__init__.py +0 -0
- onnxscript/utils/evaluation_utils.py +56 -0
- onnxscript/utils/timing_utils.py +33 -0
- onnxscript/utils/utils.py +84 -0
- onnxscript/values.py +790 -0
- onnxscript/version_converter/__init__.py +21 -0
- onnxscript/version_converter/_version_converter.py +314 -0
- onnxscript-0.1.0.dist-info/LICENSE +21 -0
- onnxscript-0.1.0.dist-info/METADATA +370 -0
- onnxscript-0.1.0.dist-info/RECORD +176 -0
- onnxscript-0.1.0.dist-info/WHEEL +5 -0
- onnxscript-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
# pylint: disable=import-outside-toplevel, no-else-raise, consider-using-with, consider-using-enumerate
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import itertools
|
|
9
|
+
import multiprocessing
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from typing import Any, Sequence
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
import onnx
|
|
20
|
+
import onnx.inliner
|
|
21
|
+
|
|
22
|
+
import onnxscript.optimizer
|
|
23
|
+
import onnxscript.rewriter
|
|
24
|
+
import onnxscript.rewriter.llama_rule_sets as rules
|
|
25
|
+
import onnxscript.rewriter.onnxruntime as ort_rules
|
|
26
|
+
import onnxscript.rewriter.pattern as orp
|
|
27
|
+
from onnxscript import ir
|
|
28
|
+
from onnxscript.optimizer._remove_unused import remove_unused_nodes
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_parsed_args(
|
|
32
|
+
name: str,
|
|
33
|
+
description: str | None = None,
|
|
34
|
+
epilog: str | None = None,
|
|
35
|
+
new_args: list[str] | None = None,
|
|
36
|
+
**kwargs: tuple[Any, str],
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
"""
|
|
39
|
+
Returns parsed arguments for examples in this package.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
name: script name
|
|
43
|
+
scenarios: list of available scenarios
|
|
44
|
+
description: parser description
|
|
45
|
+
epilog: text at the end of the parser
|
|
46
|
+
number: default value for number parameter
|
|
47
|
+
repeat: default value for repeat parameter
|
|
48
|
+
warmup: default value for warmup parameter
|
|
49
|
+
sleep: default value for sleep parameter
|
|
50
|
+
expose: if empty, keeps all the parameters,
|
|
51
|
+
if not None, only publish kwargs contains, otherwise the list
|
|
52
|
+
of parameters to publish separated by a comma
|
|
53
|
+
new_args: args to consider or None to take `sys.args`
|
|
54
|
+
kwargs: additional parameters,
|
|
55
|
+
example: `n_trees=(10, "number of trees to train")`
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
interpreted parameters in a dictionary
|
|
59
|
+
"""
|
|
60
|
+
parser = argparse.ArgumentParser(
|
|
61
|
+
prog=name,
|
|
62
|
+
description=description or f"Available options for {name}.py.",
|
|
63
|
+
epilog=epilog or "",
|
|
64
|
+
)
|
|
65
|
+
for k, v in kwargs.items():
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
f"--{k}",
|
|
68
|
+
help=f"{v[1]}, default is {v[0]}",
|
|
69
|
+
type=type(v[0]),
|
|
70
|
+
default=v[0],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
parsed = parser.parse_args(args=new_args)
|
|
74
|
+
return {k: getattr(parsed, k) for k in kwargs}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class BenchmarkError(RuntimeError):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_machine() -> dict[str, Any]:
|
|
82
|
+
"""Returns the machine specification."""
|
|
83
|
+
cpu: dict[str, Any] = dict(
|
|
84
|
+
machine=str(platform.machine()),
|
|
85
|
+
processor=str(platform.processor()),
|
|
86
|
+
version=str(sys.version),
|
|
87
|
+
cpu=int(multiprocessing.cpu_count()),
|
|
88
|
+
executable=str(sys.executable),
|
|
89
|
+
)
|
|
90
|
+
try:
|
|
91
|
+
import torch.cuda
|
|
92
|
+
except ImportError:
|
|
93
|
+
return cpu
|
|
94
|
+
|
|
95
|
+
cpu["has_cuda"] = bool(torch.cuda.is_available())
|
|
96
|
+
if cpu["has_cuda"]:
|
|
97
|
+
cpu["capability"] = torch.cuda.get_device_capability(0)
|
|
98
|
+
cpu["device_name"] = str(torch.cuda.get_device_name(0))
|
|
99
|
+
return cpu
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _cmd_line(script_name: str, **kwargs: dict[str, Any]) -> list[str]:
|
|
103
|
+
args = [sys.executable, "-m", script_name]
|
|
104
|
+
for k, v in kwargs.items():
|
|
105
|
+
args.append(f"--{k}")
|
|
106
|
+
args.append(str(v))
|
|
107
|
+
return args
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _extract_metrics(text: str) -> dict[str, str]:
|
|
111
|
+
reg = re.compile(r":(.*?),(.*.?);")
|
|
112
|
+
res = reg.findall(text)
|
|
113
|
+
if len(res) == 0:
|
|
114
|
+
return {}
|
|
115
|
+
return dict(res)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _make_prefix(script_name: str, index: int) -> str:
|
|
119
|
+
name = os.path.splitext(script_name)[0]
|
|
120
|
+
return f"{name}_dort_c{index}_"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def run_benchmark(
|
|
124
|
+
script_name: str,
|
|
125
|
+
configs: list[dict[str, Any]],
|
|
126
|
+
verbose: int = 0,
|
|
127
|
+
stop_if_exception: bool = True,
|
|
128
|
+
dump: bool = False,
|
|
129
|
+
) -> list[dict[str, Any]]:
|
|
130
|
+
"""
|
|
131
|
+
Runs a script multiple times and extract information from the output
|
|
132
|
+
following the pattern ``:<metric>,<value>;``.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
script_name: python script to run
|
|
136
|
+
configs: list of execution to do
|
|
137
|
+
stop_if_exception: stop if one experiment failed, otherwise continue
|
|
138
|
+
verbose: use tqdm to follow the progress
|
|
139
|
+
dump: dump onnx file
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
values
|
|
143
|
+
"""
|
|
144
|
+
if verbose:
|
|
145
|
+
from tqdm import tqdm
|
|
146
|
+
|
|
147
|
+
loop = tqdm(configs)
|
|
148
|
+
else:
|
|
149
|
+
loop = configs
|
|
150
|
+
|
|
151
|
+
data: list[dict[str, Any]] = []
|
|
152
|
+
for i, config in enumerate(loop):
|
|
153
|
+
cmd = _cmd_line(script_name, **config)
|
|
154
|
+
|
|
155
|
+
if dump:
|
|
156
|
+
os.environ["ONNXRT_DUMP_PATH"] = _make_prefix(script_name, i)
|
|
157
|
+
else:
|
|
158
|
+
os.environ["ONNXRT_DUMP_PATH"] = ""
|
|
159
|
+
if verbose > 3:
|
|
160
|
+
print(f"[run_benchmark] cmd={cmd if isinstance(cmd, str) else ' '.join(cmd)}")
|
|
161
|
+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
162
|
+
res = p.communicate()
|
|
163
|
+
out, err = res
|
|
164
|
+
sout = out.decode("utf-8", errors="ignore")
|
|
165
|
+
serr = err.decode("utf-8", errors="ignore")
|
|
166
|
+
|
|
167
|
+
if "ONNXRuntimeError" in serr or "ONNXRuntimeError" in sout:
|
|
168
|
+
if stop_if_exception:
|
|
169
|
+
raise RuntimeError(
|
|
170
|
+
f"Unable to continue with config {config} due to the "
|
|
171
|
+
f"following error\n{serr}"
|
|
172
|
+
f"\n----OUTPUT--\n{sout}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
metrics = _extract_metrics(sout)
|
|
176
|
+
if len(metrics) == 0:
|
|
177
|
+
if stop_if_exception:
|
|
178
|
+
raise BenchmarkError(
|
|
179
|
+
f"Unable (2) to continue with config {config}, no metric was "
|
|
180
|
+
f"collected.\n--ERROR--\n{serr}\n--OUTPUT--\n{sout}"
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
metrics = {}
|
|
184
|
+
metrics.update(config)
|
|
185
|
+
metrics["ERROR"] = serr
|
|
186
|
+
metrics["OUTPUT"] = sout
|
|
187
|
+
metrics["CMD"] = f"[{' '.join(cmd)}]"
|
|
188
|
+
data.append(metrics)
|
|
189
|
+
if verbose > 5:
|
|
190
|
+
print("--------------- ERROR")
|
|
191
|
+
print(serr)
|
|
192
|
+
if verbose >= 10:
|
|
193
|
+
print("--------------- OUTPUT")
|
|
194
|
+
print(sout)
|
|
195
|
+
|
|
196
|
+
return data
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def measure_discrepancies(
|
|
200
|
+
expected: list[tuple[Any, ...]],
|
|
201
|
+
outputs: list[tuple[Any, ...]],
|
|
202
|
+
) -> tuple[float, float]:
|
|
203
|
+
"""
|
|
204
|
+
Computes the discrepancies.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
expected: list of outputs coming from a torch model
|
|
208
|
+
outputs: list of outputs coming from an onnx model
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
max absolute errors, max relative errors
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
def _flatten(outputs):
|
|
215
|
+
flat = []
|
|
216
|
+
for tensor in outputs:
|
|
217
|
+
if isinstance(tensor, tuple):
|
|
218
|
+
flat.extend(_flatten(tensor))
|
|
219
|
+
else:
|
|
220
|
+
flat.append(tensor)
|
|
221
|
+
return tuple(flat)
|
|
222
|
+
|
|
223
|
+
abs_errs = []
|
|
224
|
+
rel_errs = []
|
|
225
|
+
for torch_outputs_mixed_types, onnx_outputs in zip(expected, outputs):
|
|
226
|
+
torch_outputs = _flatten(torch_outputs_mixed_types)
|
|
227
|
+
assert len(torch_outputs) == len(onnx_outputs), (
|
|
228
|
+
f"Length mismatch {len(torch_outputs)} != {len(onnx_outputs)}"
|
|
229
|
+
)
|
|
230
|
+
for torch_tensor, onnx_tensor in zip(torch_outputs, onnx_outputs):
|
|
231
|
+
assert torch_tensor.dtype == onnx_tensor.dtype, (
|
|
232
|
+
f"Type mismatch {torch_tensor.dtype} != {onnx_tensor.dtype}"
|
|
233
|
+
)
|
|
234
|
+
assert torch_tensor.shape == onnx_tensor.shape, (
|
|
235
|
+
f"Type mismatch {torch_tensor.shape} != {onnx_tensor.shape}"
|
|
236
|
+
)
|
|
237
|
+
diff = torch_tensor - onnx_tensor
|
|
238
|
+
abs_err = float(diff.abs().max())
|
|
239
|
+
rel_err = float((diff.abs() / torch_tensor).max())
|
|
240
|
+
abs_errs.append(abs_err)
|
|
241
|
+
rel_errs.append(rel_err)
|
|
242
|
+
return max(abs_errs), max(rel_errs)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def common_export(
|
|
246
|
+
model: Any,
|
|
247
|
+
inputs: Sequence[Any],
|
|
248
|
+
exporter: str = "dynamo",
|
|
249
|
+
target_opset: int = 18,
|
|
250
|
+
folder: str = "",
|
|
251
|
+
filename: str = "model.onnx",
|
|
252
|
+
dynamic_shapes: Any | None = None,
|
|
253
|
+
verbose: int = 0,
|
|
254
|
+
optimization: str | None = None,
|
|
255
|
+
stats: dict[str, Any] | None = None,
|
|
256
|
+
):
|
|
257
|
+
"""
|
|
258
|
+
Exports a model into a folder.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
model: model
|
|
262
|
+
exporter: script, dynamo
|
|
263
|
+
folder: folder to export into
|
|
264
|
+
filename: onnx filename
|
|
265
|
+
inputs: inputs
|
|
266
|
+
dynamic_shapes: dynamic shapes
|
|
267
|
+
target_opset: target opset
|
|
268
|
+
optimization: optimization scenario, '/' separated values
|
|
269
|
+
verbose: verbosity
|
|
270
|
+
stats: if not None, populates this
|
|
271
|
+
dictionary with statistics about time
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
onnx proto
|
|
275
|
+
|
|
276
|
+
"""
|
|
277
|
+
import torch.onnx
|
|
278
|
+
|
|
279
|
+
if folder:
|
|
280
|
+
if not os.path.exists(folder):
|
|
281
|
+
os.mkdir(folder)
|
|
282
|
+
filename = os.path.join(folder, filename)
|
|
283
|
+
|
|
284
|
+
if verbose:
|
|
285
|
+
print(f"[common_export] start exporting with {exporter!r} in {filename!r}")
|
|
286
|
+
begin = time.perf_counter()
|
|
287
|
+
if exporter == "script":
|
|
288
|
+
torch.onnx.export(
|
|
289
|
+
model,
|
|
290
|
+
inputs, # type: ignore[arg-type]
|
|
291
|
+
filename,
|
|
292
|
+
do_constant_folding=False,
|
|
293
|
+
input_names=[f"input{i}" for i in range(len(inputs))],
|
|
294
|
+
opset_version=target_opset,
|
|
295
|
+
dynamic_axes=dynamic_shapes,
|
|
296
|
+
)
|
|
297
|
+
elif exporter == "dynamo":
|
|
298
|
+
assert dynamic_shapes is None, (
|
|
299
|
+
f"dynamic_shapes={dynamic_shapes} is not implemented yet"
|
|
300
|
+
)
|
|
301
|
+
with torch.no_grad():
|
|
302
|
+
prog = torch.onnx.dynamo_export(model, *inputs)
|
|
303
|
+
onnx.save(prog.model_proto, filename)
|
|
304
|
+
else:
|
|
305
|
+
raise ValueError(f"Unknown exporter {exporter!r}")
|
|
306
|
+
|
|
307
|
+
if stats is not None:
|
|
308
|
+
stats["export_time"] = time.perf_counter() - begin
|
|
309
|
+
stats["filesize"] = os.stat(filename).st_size
|
|
310
|
+
|
|
311
|
+
if verbose:
|
|
312
|
+
print(f"[common_export] exporter done in {time.perf_counter() - begin}s")
|
|
313
|
+
print(f"[common_export] size of the export: {os.stat(filename).st_size / 2**20} Mb")
|
|
314
|
+
|
|
315
|
+
with open(filename, "rb") as f:
|
|
316
|
+
onx = onnx.load(f)
|
|
317
|
+
|
|
318
|
+
if optimization:
|
|
319
|
+
if verbose:
|
|
320
|
+
print(f"[common_export] start optimization with {optimization!r}")
|
|
321
|
+
begin = time.perf_counter()
|
|
322
|
+
optimized_model = optimize_model_proto(onx, optimization, verbose=verbose, stats=stats)
|
|
323
|
+
end = time.perf_counter() - begin
|
|
324
|
+
if stats is not None:
|
|
325
|
+
stats["optimization_time"] = end
|
|
326
|
+
if verbose:
|
|
327
|
+
print(f"[common_export] optimization done in {end}")
|
|
328
|
+
print(f"[common_export] saves the model in {filename!r}")
|
|
329
|
+
begin = time.perf_counter()
|
|
330
|
+
|
|
331
|
+
onnx.save(optimized_model, filename)
|
|
332
|
+
if verbose:
|
|
333
|
+
print(f"[common_export] done saving in {time.perf_counter() - begin}")
|
|
334
|
+
|
|
335
|
+
return onx
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def apply_rule_sets(
|
|
339
|
+
model_proto: onnx.ModelProto,
|
|
340
|
+
rule_sets: list[str],
|
|
341
|
+
stats: dict[str, Any] | None = None,
|
|
342
|
+
verbose: int = 0,
|
|
343
|
+
):
|
|
344
|
+
"""
|
|
345
|
+
Applies set of patterns on a model to optimizes.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
model_proto: model
|
|
349
|
+
rule_sets: sets ot apply
|
|
350
|
+
stats: add statistics if not empty
|
|
351
|
+
verbose: verbosity
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
optimized model
|
|
355
|
+
"""
|
|
356
|
+
assert rule_sets, "No need to call apply_rule_sets for an empty set."
|
|
357
|
+
if verbose:
|
|
358
|
+
print(f"[apply_rule_sets] deserialize model before {rule_sets}")
|
|
359
|
+
begin = time.perf_counter()
|
|
360
|
+
ir_model = ir.serde.deserialize_model(model_proto)
|
|
361
|
+
end = time.perf_counter() - begin
|
|
362
|
+
if stats is not None:
|
|
363
|
+
stats["deserialize_time"] = end
|
|
364
|
+
if verbose:
|
|
365
|
+
print(f"[apply_rule_sets] deserialize done in {end}")
|
|
366
|
+
|
|
367
|
+
for rule_set_name in rule_sets:
|
|
368
|
+
if verbose:
|
|
369
|
+
print(f"[apply_rule_sets] applies {rule_set_name!r}")
|
|
370
|
+
|
|
371
|
+
if rule_set_name == "llama0":
|
|
372
|
+
rule_set = rules.llama_p0_rule_set()
|
|
373
|
+
elif rule_set_name == "onnxruntime":
|
|
374
|
+
rule_set = orp.RewriteRuleSet(ort_rules.ORT_PATTERN_REWRITE_RULES)
|
|
375
|
+
else:
|
|
376
|
+
raise ValueError(f"Unexpected rule_set name {rule_set_name!r}")
|
|
377
|
+
|
|
378
|
+
begin = time.perf_counter()
|
|
379
|
+
rule_set.apply_to_model(ir_model)
|
|
380
|
+
remove_unused_nodes(ir_model)
|
|
381
|
+
end = time.perf_counter() - begin
|
|
382
|
+
if stats is not None:
|
|
383
|
+
stats[f"opt_rule_{rule_set_name}_time"] = end
|
|
384
|
+
if verbose:
|
|
385
|
+
print(f"[apply_rule_sets] {rule_set_name} done in {end}")
|
|
386
|
+
|
|
387
|
+
if verbose:
|
|
388
|
+
print("[apply_rule_sets] serialize model")
|
|
389
|
+
begin = time.perf_counter()
|
|
390
|
+
rewritten_model = ir.serde.serialize_model(ir_model)
|
|
391
|
+
end = time.perf_counter() - begin
|
|
392
|
+
if stats is not None:
|
|
393
|
+
stats["serialize_time"] = end
|
|
394
|
+
if verbose:
|
|
395
|
+
print(f"[apply_rule_sets] serialize done in {end}")
|
|
396
|
+
|
|
397
|
+
if verbose:
|
|
398
|
+
print("[apply_rule_sets] remove unused")
|
|
399
|
+
begin = time.perf_counter()
|
|
400
|
+
|
|
401
|
+
remove_unused_nodes(rewritten_model)
|
|
402
|
+
|
|
403
|
+
end = time.perf_counter() - begin
|
|
404
|
+
if stats is not None:
|
|
405
|
+
stats["opt_remove_unused_time"] = end
|
|
406
|
+
if verbose:
|
|
407
|
+
print(f"[apply_rule_sets] remove unused done in {end}")
|
|
408
|
+
|
|
409
|
+
return rewritten_model
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def optimize_model_proto(
|
|
413
|
+
model_proto: onnx.ModelProto,
|
|
414
|
+
optimization: str | None = None,
|
|
415
|
+
verbose: int = 0,
|
|
416
|
+
stats: dict[str, Any] | None = None,
|
|
417
|
+
):
|
|
418
|
+
"""
|
|
419
|
+
Optimizes a model given some scenarios.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
model_proto: ModelProto
|
|
423
|
+
optimization: '/' separated value
|
|
424
|
+
verbose: verbosity
|
|
425
|
+
stats: if not None, populates this dictionary with statistics
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
optmized model
|
|
429
|
+
"""
|
|
430
|
+
if not optimization:
|
|
431
|
+
return model_proto
|
|
432
|
+
|
|
433
|
+
known_rule_sets = {"llama0", "onnxruntime"}
|
|
434
|
+
|
|
435
|
+
rule_sets: list[str] = []
|
|
436
|
+
for value in optimization.split("/"):
|
|
437
|
+
if value in known_rule_sets:
|
|
438
|
+
rule_sets.append(value)
|
|
439
|
+
continue
|
|
440
|
+
if value not in known_rule_sets and rule_sets:
|
|
441
|
+
model_proto = apply_rule_sets(model_proto, rule_sets, stats=stats, verbose=verbose)
|
|
442
|
+
del rule_sets[:]
|
|
443
|
+
continue
|
|
444
|
+
|
|
445
|
+
if verbose:
|
|
446
|
+
print(f"[optimize_model_proto] start {value}")
|
|
447
|
+
|
|
448
|
+
n_nodes = len(model_proto.graph.node)
|
|
449
|
+
n_functions = len(model_proto.functions)
|
|
450
|
+
begin = time.perf_counter()
|
|
451
|
+
|
|
452
|
+
if value == "optimize":
|
|
453
|
+
model_proto = onnxscript.optimizer.optimize(
|
|
454
|
+
model_proto,
|
|
455
|
+
num_iterations=2,
|
|
456
|
+
onnx_shape_inference=False,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
elif value == "rewrite":
|
|
460
|
+
model_proto = onnxscript.rewriter.rewrite(model_proto)
|
|
461
|
+
|
|
462
|
+
elif value == "inline":
|
|
463
|
+
model_proto = onnx.inliner.inline_local_functions(model_proto)
|
|
464
|
+
|
|
465
|
+
else:
|
|
466
|
+
raise AssertionError(
|
|
467
|
+
f"Optimization step {value!r} is not implemented in {optimization!r}"
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
end = time.perf_counter() - begin
|
|
471
|
+
delta = len(model_proto.graph.node) - n_nodes
|
|
472
|
+
deltaf = len(model_proto.functions) - n_functions
|
|
473
|
+
if stats:
|
|
474
|
+
stats[f"opt_{value}_time"] = end
|
|
475
|
+
stats[f"opt_{value}_dnodes"] = delta
|
|
476
|
+
stats[f"opt_{value}_dfunctions"] = deltaf
|
|
477
|
+
if verbose:
|
|
478
|
+
print(
|
|
479
|
+
f"[optimize_model_proto] {value} done in {end} "
|
|
480
|
+
f"with +/- {delta} nodes, +/- {deltaf} functions"
|
|
481
|
+
)
|
|
482
|
+
if rule_sets:
|
|
483
|
+
model_proto = apply_rule_sets(model_proto, rule_sets, stats=stats, verbose=verbose)
|
|
484
|
+
|
|
485
|
+
return model_proto
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def run_inference(
|
|
489
|
+
model: Any,
|
|
490
|
+
example_inputs: Sequence[Any],
|
|
491
|
+
warmup: int = 5,
|
|
492
|
+
repeat: int = 5,
|
|
493
|
+
verbose: int = 0,
|
|
494
|
+
) -> dict[str, Any]:
|
|
495
|
+
"""
|
|
496
|
+
Runs multiple times the same inference.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
model: torch model to run
|
|
500
|
+
example_inputs: dummy inputs
|
|
501
|
+
warmup: number of iterations to warmup
|
|
502
|
+
repeat: number of iterations to repeat
|
|
503
|
+
verbose: verbosity
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
statistcs
|
|
507
|
+
"""
|
|
508
|
+
if verbose:
|
|
509
|
+
print(f"[run_inference] start {warmup} warmup iterations")
|
|
510
|
+
|
|
511
|
+
stats: dict[str, Any] = {}
|
|
512
|
+
iterations: list[float] = []
|
|
513
|
+
begin = time.perf_counter()
|
|
514
|
+
for i in range(warmup):
|
|
515
|
+
t0 = time.perf_counter()
|
|
516
|
+
model(*example_inputs[i % len(example_inputs)])
|
|
517
|
+
iterations.append(time.perf_counter() - t0)
|
|
518
|
+
end = time.perf_counter() - begin
|
|
519
|
+
stats["warmup"] = warmup
|
|
520
|
+
stats["warmup_time"] = end
|
|
521
|
+
stats["warmup_iter"] = iterations
|
|
522
|
+
|
|
523
|
+
if verbose:
|
|
524
|
+
print(f"[run_inference] warmup done in {time.perf_counter() - begin}")
|
|
525
|
+
print(f"[run_inference] start {repeat} iterations")
|
|
526
|
+
|
|
527
|
+
iterations = []
|
|
528
|
+
begin = time.perf_counter()
|
|
529
|
+
for i in range(warmup):
|
|
530
|
+
t0 = time.perf_counter()
|
|
531
|
+
model(*example_inputs[i % len(example_inputs)])
|
|
532
|
+
iterations.append(time.perf_counter() - t0)
|
|
533
|
+
end = time.perf_counter() - begin
|
|
534
|
+
stats["repeat"] = repeat
|
|
535
|
+
stats["repeat_time"] = end
|
|
536
|
+
stats["repeat_iter"] = iterations
|
|
537
|
+
|
|
538
|
+
if verbose:
|
|
539
|
+
print(f"[run_inference] measure done in {time.perf_counter() - begin}")
|
|
540
|
+
|
|
541
|
+
return stats
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
class WrapInferenceSessionForTorch:
|
|
545
|
+
def __init__(self, sess: Any):
|
|
546
|
+
# onnxruntime is importing when needed as it takes a couple of seconds if it contains CUDA EP.
|
|
547
|
+
import onnxruntime
|
|
548
|
+
import torch
|
|
549
|
+
from onnxruntime.capi import _pybind_state as ORTC # noqa: N812
|
|
550
|
+
|
|
551
|
+
self.sess = sess
|
|
552
|
+
self.input_names = [i.name for i in sess.get_inputs()]
|
|
553
|
+
self.output_names = [i.name for i in sess.get_outputs()]
|
|
554
|
+
self.bind = onnxruntime.SessionIOBinding(sess._sess)
|
|
555
|
+
self.OrtValue = ORTC.OrtValue
|
|
556
|
+
self.ORTC = ORTC
|
|
557
|
+
self.torch = torch
|
|
558
|
+
self.run_options = onnxruntime.RunOptions()
|
|
559
|
+
|
|
560
|
+
self.TORCH_DTYPE_TO_NUMPY_DTYPE = {
|
|
561
|
+
torch.float16: np.float16,
|
|
562
|
+
torch.float32: np.float32,
|
|
563
|
+
torch.float64: np.float64,
|
|
564
|
+
torch.uint8: np.uint8,
|
|
565
|
+
torch.int8: np.int8,
|
|
566
|
+
torch.int16: np.int16,
|
|
567
|
+
torch.int32: np.int32,
|
|
568
|
+
torch.int64: np.int64,
|
|
569
|
+
torch.bool: np.bool_,
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
DEVICES = {
|
|
573
|
+
-1: ORTC.OrtDevice(ORTC.OrtDevice.cpu(), ORTC.OrtDevice.default_memory(), 0)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if torch.cuda.is_available():
|
|
577
|
+
for i in range(torch.cuda.device_count()):
|
|
578
|
+
DEVICES[i] = ORTC.OrtDevice(
|
|
579
|
+
ORTC.OrtDevice.cuda(), ORTC.OrtDevice.default_memory(), i
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
self.DEVICES = DEVICES
|
|
583
|
+
|
|
584
|
+
def _get_ortvalues_from_torch_tensors(
|
|
585
|
+
self,
|
|
586
|
+
tensors: tuple[Any, ...], # tuple["torch.Tensor", ...],
|
|
587
|
+
n_outputs: int,
|
|
588
|
+
) -> tuple[Any, Any]: # tuple[tuple["torch.Tensor", ...], tuple["OrtDevice", ...]]:
|
|
589
|
+
ortvalues = self.ORTC.OrtValueVector()
|
|
590
|
+
ortvalues.reserve(len(tensors))
|
|
591
|
+
dtypes = []
|
|
592
|
+
shapes = []
|
|
593
|
+
data_ptrs = []
|
|
594
|
+
devices = []
|
|
595
|
+
|
|
596
|
+
max_device = -1
|
|
597
|
+
assert isinstance(max_device, int), f"unexpected type for device={max_device!r}"
|
|
598
|
+
assert tensors is not None, "tensors cannot be None"
|
|
599
|
+
new_tensors = []
|
|
600
|
+
for tensor in tensors:
|
|
601
|
+
assert isinstance(tensor, self.torch.Tensor), f"Unexpected type {type(tensor)}"
|
|
602
|
+
dtypes.append(self.TORCH_DTYPE_TO_NUMPY_DTYPE[tensor.dtype])
|
|
603
|
+
shapes.append(tensor.size())
|
|
604
|
+
data_ptrs.append(tensor.data_ptr())
|
|
605
|
+
d = tensor.get_device()
|
|
606
|
+
devices.append(self.DEVICES[d])
|
|
607
|
+
new_tensors.append(tensor)
|
|
608
|
+
max_device = max(max_device, tensor.get_device())
|
|
609
|
+
|
|
610
|
+
ortvalues.push_back_batch(new_tensors, data_ptrs, dtypes, shapes, devices)
|
|
611
|
+
output_devices = []
|
|
612
|
+
for _ in range(n_outputs):
|
|
613
|
+
dev = self.DEVICES[max_device]
|
|
614
|
+
output_devices.append(dev)
|
|
615
|
+
|
|
616
|
+
return ortvalues, output_devices
|
|
617
|
+
|
|
618
|
+
def _ortvalues_to_torch_tensor(
|
|
619
|
+
self,
|
|
620
|
+
ortvalues: Any, # "onnxruntime.OrtValueVector",
|
|
621
|
+
) -> tuple[Any, ...]: # tuple["torch.Tensor", ...]:
|
|
622
|
+
if len(ortvalues) == 0:
|
|
623
|
+
return tuple()
|
|
624
|
+
|
|
625
|
+
from torch._C import _from_dlpack
|
|
626
|
+
|
|
627
|
+
if all(map(lambda i: ortvalues[i].has_value(), range(len(ortvalues)))): # noqa: C417
|
|
628
|
+
res = ortvalues.to_dlpacks(_from_dlpack)
|
|
629
|
+
else:
|
|
630
|
+
res = []
|
|
631
|
+
for i in range(len(ortvalues)):
|
|
632
|
+
res.append(
|
|
633
|
+
_from_dlpack(ortvalues[i].to_dlpack())
|
|
634
|
+
if ortvalues[i].has_value()
|
|
635
|
+
else None
|
|
636
|
+
)
|
|
637
|
+
return tuple(res)
|
|
638
|
+
|
|
639
|
+
def run(self, output_names, feeds):
|
|
640
|
+
inputs = [feeds[i] for i in self.input_names]
|
|
641
|
+
return self.run_dlpack(*inputs, output_names=output_names)
|
|
642
|
+
|
|
643
|
+
def run_dlpack(self, *inputs, output_names=None):
|
|
644
|
+
if output_names is None:
|
|
645
|
+
output_names = self.output_names
|
|
646
|
+
ortvalues, output_devices = self._get_ortvalues_from_torch_tensors(
|
|
647
|
+
inputs, len(output_names)
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
ort_outputs = self.ORTC.OrtValueVector()
|
|
651
|
+
self.sess.run_with_ortvaluevector(
|
|
652
|
+
self.run_options,
|
|
653
|
+
self.input_names,
|
|
654
|
+
ortvalues,
|
|
655
|
+
output_names,
|
|
656
|
+
ort_outputs,
|
|
657
|
+
output_devices,
|
|
658
|
+
)
|
|
659
|
+
pth_outputs = self._ortvalues_to_torch_tensor(ort_outputs)
|
|
660
|
+
return pth_outputs
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def run_onnx_inference(
|
|
664
|
+
model: onnx.ModelProto,
|
|
665
|
+
example_inputs: Sequence[Any],
|
|
666
|
+
warmup: int = 5,
|
|
667
|
+
repeat: int = 5,
|
|
668
|
+
verbose: int = 0,
|
|
669
|
+
ort_optimize: bool = True,
|
|
670
|
+
torch_model: Any | None = None,
|
|
671
|
+
) -> dict[str, Any]:
|
|
672
|
+
"""
|
|
673
|
+
Runs multiple times the same inference with onnxruntime.
|
|
674
|
+
|
|
675
|
+
Args:
|
|
676
|
+
model: torch model to run
|
|
677
|
+
example_inputs: dummy inputs
|
|
678
|
+
warmup: number of iterations to warmup
|
|
679
|
+
repeat: number of iterations to repeat
|
|
680
|
+
verbose: verbosity
|
|
681
|
+
ort_optimize: enable, disable onnxruntime optimizations
|
|
682
|
+
torch_model: if not empty, measure the discrepancies
|
|
683
|
+
|
|
684
|
+
Returns:
|
|
685
|
+
statistcs
|
|
686
|
+
"""
|
|
687
|
+
stats: dict[str, Any] = {}
|
|
688
|
+
device = example_inputs[0][0].get_device()
|
|
689
|
+
providers = (
|
|
690
|
+
["CUDAExecutionProvider", "CPUExecutionProvider"]
|
|
691
|
+
if device >= 0
|
|
692
|
+
else ["CPUExecutionProvider"]
|
|
693
|
+
)
|
|
694
|
+
stats["providers"] = ",".join(providers)
|
|
695
|
+
if verbose:
|
|
696
|
+
print(f"[run_inference] create session with providers {providers!r}")
|
|
697
|
+
|
|
698
|
+
begin = time.perf_counter()
|
|
699
|
+
# onnxruntime is importing when needed as it takes a couple of seconds if it contains CUDA EP.
|
|
700
|
+
import onnxruntime
|
|
701
|
+
|
|
702
|
+
so = onnxruntime.SessionOptions()
|
|
703
|
+
if ort_optimize:
|
|
704
|
+
so.add_session_config_entry("session.disable_aot_function_inlining", "0")
|
|
705
|
+
so.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
|
|
706
|
+
else:
|
|
707
|
+
so.add_session_config_entry("session.disable_aot_function_inlining", "1")
|
|
708
|
+
so.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_DISABLE_ALL
|
|
709
|
+
|
|
710
|
+
sess = onnxruntime.InferenceSession(model.SerializeToString(), so, providers)
|
|
711
|
+
wrapped_session = WrapInferenceSessionForTorch(sess)
|
|
712
|
+
|
|
713
|
+
end = time.perf_counter() - begin
|
|
714
|
+
stats["ort_session_create_time"] = end
|
|
715
|
+
if verbose:
|
|
716
|
+
print(f"[run_inference] created session in {end}")
|
|
717
|
+
print(f"[run_inference] start {warmup} warmup iterations")
|
|
718
|
+
|
|
719
|
+
if torch_model:
|
|
720
|
+
expected = [
|
|
721
|
+
torch_model(*example_inputs[i % len(example_inputs)]) for i in range(warmup)
|
|
722
|
+
]
|
|
723
|
+
|
|
724
|
+
got = []
|
|
725
|
+
iterations = []
|
|
726
|
+
begin = time.perf_counter()
|
|
727
|
+
for i in range(warmup):
|
|
728
|
+
t0 = time.perf_counter()
|
|
729
|
+
got.append(wrapped_session.run_dlpack(*example_inputs[i % len(example_inputs)]))
|
|
730
|
+
iterations.append(time.perf_counter() - t0)
|
|
731
|
+
end = time.perf_counter() - begin
|
|
732
|
+
stats["warmup"] = warmup
|
|
733
|
+
stats["warmup_time"] = end / warmup
|
|
734
|
+
stats["warmup_iter"] = iterations
|
|
735
|
+
if torch_model:
|
|
736
|
+
abs_err, rel_err = measure_discrepancies(expected, got)
|
|
737
|
+
stats["discrepancies_abs"] = abs_err
|
|
738
|
+
stats["discrepancies_rel"] = rel_err
|
|
739
|
+
|
|
740
|
+
if verbose:
|
|
741
|
+
print(f"[run_inference] warmup done in {time.perf_counter() - begin}")
|
|
742
|
+
print(f"[run_inference] start {repeat} iterations")
|
|
743
|
+
|
|
744
|
+
iterations = []
|
|
745
|
+
begin = time.perf_counter()
|
|
746
|
+
for i in range(repeat):
|
|
747
|
+
t0 = time.perf_counter()
|
|
748
|
+
wrapped_session.run_dlpack(*example_inputs[i % len(example_inputs)])
|
|
749
|
+
iterations.append(time.perf_counter() - t0)
|
|
750
|
+
end = time.perf_counter() - begin
|
|
751
|
+
stats["repeat"] = repeat
|
|
752
|
+
stats["repeat_time"] = end / repeat
|
|
753
|
+
stats["repeat_iter"] = iterations
|
|
754
|
+
|
|
755
|
+
if verbose:
|
|
756
|
+
print(f"[run_inference] measure done in {time.perf_counter() - begin}")
|
|
757
|
+
|
|
758
|
+
return stats
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def multi_run(kwargs: dict[str, Any]) -> bool:
|
|
762
|
+
"""Checks if multiple values were sent for one argument."""
|
|
763
|
+
return any(isinstance(v, str) and "," in v for v in kwargs.values())
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def make_configs(kwargs: dict[str, Any]) -> list[dict[str, Any]]:
|
|
767
|
+
"""Creates all the configurations based on the command line arguments."""
|
|
768
|
+
print(kwargs)
|
|
769
|
+
args = []
|
|
770
|
+
for k, v in kwargs.items():
|
|
771
|
+
if isinstance(v, str):
|
|
772
|
+
args.append([(k, s) for s in v.split(",")])
|
|
773
|
+
else:
|
|
774
|
+
args.append([(k, v)])
|
|
775
|
+
configs = list(itertools.product(*args))
|
|
776
|
+
return [dict(c) for c in configs]
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def make_dataframe_from_benchmark_data(data: list[dict]) -> Any:
|
|
780
|
+
"""Creates a dataframe from the received data."""
|
|
781
|
+
import pandas
|
|
782
|
+
|
|
783
|
+
return pandas.DataFrame(data)
|