triton-model-analyzer 1.48.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.
- model_analyzer/__init__.py +15 -0
- model_analyzer/analyzer.py +448 -0
- model_analyzer/cli/__init__.py +15 -0
- model_analyzer/cli/cli.py +193 -0
- model_analyzer/config/__init__.py +15 -0
- model_analyzer/config/generate/__init__.py +15 -0
- model_analyzer/config/generate/automatic_model_config_generator.py +164 -0
- model_analyzer/config/generate/base_model_config_generator.py +352 -0
- model_analyzer/config/generate/brute_plus_binary_parameter_search_run_config_generator.py +164 -0
- model_analyzer/config/generate/brute_run_config_generator.py +154 -0
- model_analyzer/config/generate/concurrency_sweeper.py +75 -0
- model_analyzer/config/generate/config_generator_interface.py +52 -0
- model_analyzer/config/generate/coordinate.py +143 -0
- model_analyzer/config/generate/coordinate_data.py +86 -0
- model_analyzer/config/generate/generator_utils.py +116 -0
- model_analyzer/config/generate/manual_model_config_generator.py +187 -0
- model_analyzer/config/generate/model_config_generator_factory.py +92 -0
- model_analyzer/config/generate/model_profile_spec.py +74 -0
- model_analyzer/config/generate/model_run_config_generator.py +154 -0
- model_analyzer/config/generate/model_variant_name_manager.py +150 -0
- model_analyzer/config/generate/neighborhood.py +536 -0
- model_analyzer/config/generate/optuna_plus_concurrency_sweep_run_config_generator.py +141 -0
- model_analyzer/config/generate/optuna_run_config_generator.py +838 -0
- model_analyzer/config/generate/perf_analyzer_config_generator.py +312 -0
- model_analyzer/config/generate/quick_plus_concurrency_sweep_run_config_generator.py +130 -0
- model_analyzer/config/generate/quick_run_config_generator.py +753 -0
- model_analyzer/config/generate/run_config_generator_factory.py +329 -0
- model_analyzer/config/generate/search_config.py +112 -0
- model_analyzer/config/generate/search_dimension.py +73 -0
- model_analyzer/config/generate/search_dimensions.py +85 -0
- model_analyzer/config/generate/search_parameter.py +49 -0
- model_analyzer/config/generate/search_parameters.py +388 -0
- model_analyzer/config/input/__init__.py +15 -0
- model_analyzer/config/input/config_command.py +483 -0
- model_analyzer/config/input/config_command_profile.py +1747 -0
- model_analyzer/config/input/config_command_report.py +267 -0
- model_analyzer/config/input/config_defaults.py +236 -0
- model_analyzer/config/input/config_enum.py +83 -0
- model_analyzer/config/input/config_field.py +216 -0
- model_analyzer/config/input/config_list_generic.py +112 -0
- model_analyzer/config/input/config_list_numeric.py +151 -0
- model_analyzer/config/input/config_list_string.py +111 -0
- model_analyzer/config/input/config_none.py +71 -0
- model_analyzer/config/input/config_object.py +129 -0
- model_analyzer/config/input/config_primitive.py +81 -0
- model_analyzer/config/input/config_status.py +75 -0
- model_analyzer/config/input/config_sweep.py +83 -0
- model_analyzer/config/input/config_union.py +113 -0
- model_analyzer/config/input/config_utils.py +128 -0
- model_analyzer/config/input/config_value.py +243 -0
- model_analyzer/config/input/objects/__init__.py +15 -0
- model_analyzer/config/input/objects/config_model_profile_spec.py +325 -0
- model_analyzer/config/input/objects/config_model_report_spec.py +173 -0
- model_analyzer/config/input/objects/config_plot.py +198 -0
- model_analyzer/config/input/objects/config_protobuf_utils.py +101 -0
- model_analyzer/config/input/yaml_config_validator.py +82 -0
- model_analyzer/config/run/__init__.py +15 -0
- model_analyzer/config/run/model_run_config.py +313 -0
- model_analyzer/config/run/run_config.py +168 -0
- model_analyzer/constants.py +76 -0
- model_analyzer/device/__init__.py +15 -0
- model_analyzer/device/device.py +24 -0
- model_analyzer/device/gpu_device.py +87 -0
- model_analyzer/device/gpu_device_factory.py +248 -0
- model_analyzer/entrypoint.py +307 -0
- model_analyzer/log_formatter.py +65 -0
- model_analyzer/model_analyzer_exceptions.py +24 -0
- model_analyzer/model_manager.py +255 -0
- model_analyzer/monitor/__init__.py +15 -0
- model_analyzer/monitor/cpu_monitor.py +69 -0
- model_analyzer/monitor/dcgm/DcgmDiag.py +191 -0
- model_analyzer/monitor/dcgm/DcgmFieldGroup.py +83 -0
- model_analyzer/monitor/dcgm/DcgmGroup.py +815 -0
- model_analyzer/monitor/dcgm/DcgmHandle.py +141 -0
- model_analyzer/monitor/dcgm/DcgmJsonReader.py +69 -0
- model_analyzer/monitor/dcgm/DcgmReader.py +623 -0
- model_analyzer/monitor/dcgm/DcgmStatus.py +57 -0
- model_analyzer/monitor/dcgm/DcgmSystem.py +412 -0
- model_analyzer/monitor/dcgm/__init__.py +15 -0
- model_analyzer/monitor/dcgm/common/__init__.py +13 -0
- model_analyzer/monitor/dcgm/common/dcgm_client_cli_parser.py +194 -0
- model_analyzer/monitor/dcgm/common/dcgm_client_main.py +86 -0
- model_analyzer/monitor/dcgm/dcgm_agent.py +887 -0
- model_analyzer/monitor/dcgm/dcgm_collectd_plugin.py +369 -0
- model_analyzer/monitor/dcgm/dcgm_errors.py +395 -0
- model_analyzer/monitor/dcgm/dcgm_field_helpers.py +546 -0
- model_analyzer/monitor/dcgm/dcgm_fields.py +815 -0
- model_analyzer/monitor/dcgm/dcgm_fields_collectd.py +671 -0
- model_analyzer/monitor/dcgm/dcgm_fields_internal.py +29 -0
- model_analyzer/monitor/dcgm/dcgm_fluentd.py +45 -0
- model_analyzer/monitor/dcgm/dcgm_monitor.py +138 -0
- model_analyzer/monitor/dcgm/dcgm_prometheus.py +326 -0
- model_analyzer/monitor/dcgm/dcgm_structs.py +2357 -0
- model_analyzer/monitor/dcgm/dcgm_telegraf.py +65 -0
- model_analyzer/monitor/dcgm/dcgm_value.py +151 -0
- model_analyzer/monitor/dcgm/dcgmvalue.py +155 -0
- model_analyzer/monitor/dcgm/denylist_recommendations.py +573 -0
- model_analyzer/monitor/dcgm/pydcgm.py +47 -0
- model_analyzer/monitor/monitor.py +143 -0
- model_analyzer/monitor/remote_monitor.py +137 -0
- model_analyzer/output/__init__.py +15 -0
- model_analyzer/output/file_writer.py +63 -0
- model_analyzer/output/output_writer.py +42 -0
- model_analyzer/perf_analyzer/__init__.py +15 -0
- model_analyzer/perf_analyzer/genai_perf_config.py +206 -0
- model_analyzer/perf_analyzer/perf_analyzer.py +882 -0
- model_analyzer/perf_analyzer/perf_config.py +479 -0
- model_analyzer/plots/__init__.py +15 -0
- model_analyzer/plots/detailed_plot.py +266 -0
- model_analyzer/plots/plot_manager.py +224 -0
- model_analyzer/plots/simple_plot.py +213 -0
- model_analyzer/record/__init__.py +15 -0
- model_analyzer/record/gpu_record.py +68 -0
- model_analyzer/record/metrics_manager.py +887 -0
- model_analyzer/record/record.py +280 -0
- model_analyzer/record/record_aggregator.py +256 -0
- model_analyzer/record/types/__init__.py +15 -0
- model_analyzer/record/types/cpu_available_ram.py +93 -0
- model_analyzer/record/types/cpu_used_ram.py +93 -0
- model_analyzer/record/types/gpu_free_memory.py +96 -0
- model_analyzer/record/types/gpu_power_usage.py +107 -0
- model_analyzer/record/types/gpu_total_memory.py +96 -0
- model_analyzer/record/types/gpu_used_memory.py +96 -0
- model_analyzer/record/types/gpu_utilization.py +108 -0
- model_analyzer/record/types/inter_token_latency_avg.py +60 -0
- model_analyzer/record/types/inter_token_latency_base.py +74 -0
- model_analyzer/record/types/inter_token_latency_max.py +60 -0
- model_analyzer/record/types/inter_token_latency_min.py +60 -0
- model_analyzer/record/types/inter_token_latency_p25.py +60 -0
- model_analyzer/record/types/inter_token_latency_p50.py +60 -0
- model_analyzer/record/types/inter_token_latency_p75.py +60 -0
- model_analyzer/record/types/inter_token_latency_p90.py +60 -0
- model_analyzer/record/types/inter_token_latency_p95.py +60 -0
- model_analyzer/record/types/inter_token_latency_p99.py +60 -0
- model_analyzer/record/types/output_token_throughput.py +105 -0
- model_analyzer/record/types/perf_client_response_wait.py +97 -0
- model_analyzer/record/types/perf_client_send_recv.py +97 -0
- model_analyzer/record/types/perf_latency.py +111 -0
- model_analyzer/record/types/perf_latency_avg.py +60 -0
- model_analyzer/record/types/perf_latency_base.py +74 -0
- model_analyzer/record/types/perf_latency_p90.py +60 -0
- model_analyzer/record/types/perf_latency_p95.py +60 -0
- model_analyzer/record/types/perf_latency_p99.py +60 -0
- model_analyzer/record/types/perf_server_compute_infer.py +97 -0
- model_analyzer/record/types/perf_server_compute_input.py +97 -0
- model_analyzer/record/types/perf_server_compute_output.py +97 -0
- model_analyzer/record/types/perf_server_queue.py +97 -0
- model_analyzer/record/types/perf_throughput.py +105 -0
- model_analyzer/record/types/time_to_first_token_avg.py +60 -0
- model_analyzer/record/types/time_to_first_token_base.py +74 -0
- model_analyzer/record/types/time_to_first_token_max.py +60 -0
- model_analyzer/record/types/time_to_first_token_min.py +60 -0
- model_analyzer/record/types/time_to_first_token_p25.py +60 -0
- model_analyzer/record/types/time_to_first_token_p50.py +60 -0
- model_analyzer/record/types/time_to_first_token_p75.py +60 -0
- model_analyzer/record/types/time_to_first_token_p90.py +60 -0
- model_analyzer/record/types/time_to_first_token_p95.py +60 -0
- model_analyzer/record/types/time_to_first_token_p99.py +60 -0
- model_analyzer/reports/__init__.py +15 -0
- model_analyzer/reports/html_report.py +195 -0
- model_analyzer/reports/pdf_report.py +50 -0
- model_analyzer/reports/report.py +86 -0
- model_analyzer/reports/report_factory.py +62 -0
- model_analyzer/reports/report_manager.py +1376 -0
- model_analyzer/reports/report_utils.py +42 -0
- model_analyzer/result/__init__.py +15 -0
- model_analyzer/result/constraint_manager.py +150 -0
- model_analyzer/result/model_config_measurement.py +354 -0
- model_analyzer/result/model_constraints.py +105 -0
- model_analyzer/result/parameter_search.py +246 -0
- model_analyzer/result/result_manager.py +430 -0
- model_analyzer/result/result_statistics.py +159 -0
- model_analyzer/result/result_table.py +217 -0
- model_analyzer/result/result_table_manager.py +646 -0
- model_analyzer/result/result_utils.py +42 -0
- model_analyzer/result/results.py +277 -0
- model_analyzer/result/run_config_measurement.py +658 -0
- model_analyzer/result/run_config_result.py +210 -0
- model_analyzer/result/run_config_result_comparator.py +110 -0
- model_analyzer/result/sorted_results.py +151 -0
- model_analyzer/state/__init__.py +15 -0
- model_analyzer/state/analyzer_state.py +76 -0
- model_analyzer/state/analyzer_state_manager.py +215 -0
- model_analyzer/triton/__init__.py +15 -0
- model_analyzer/triton/client/__init__.py +15 -0
- model_analyzer/triton/client/client.py +234 -0
- model_analyzer/triton/client/client_factory.py +57 -0
- model_analyzer/triton/client/grpc_client.py +104 -0
- model_analyzer/triton/client/http_client.py +107 -0
- model_analyzer/triton/model/__init__.py +15 -0
- model_analyzer/triton/model/model_config.py +556 -0
- model_analyzer/triton/model/model_config_variant.py +29 -0
- model_analyzer/triton/server/__init__.py +15 -0
- model_analyzer/triton/server/server.py +76 -0
- model_analyzer/triton/server/server_config.py +269 -0
- model_analyzer/triton/server/server_docker.py +229 -0
- model_analyzer/triton/server/server_factory.py +306 -0
- model_analyzer/triton/server/server_local.py +158 -0
- triton_model_analyzer-1.48.0.dist-info/METADATA +52 -0
- triton_model_analyzer-1.48.0.dist-info/RECORD +204 -0
- triton_model_analyzer-1.48.0.dist-info/WHEEL +5 -0
- triton_model_analyzer-1.48.0.dist-info/entry_points.txt +2 -0
- triton_model_analyzer-1.48.0.dist-info/licenses/LICENSE +67 -0
- triton_model_analyzer-1.48.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# 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, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
import importlib
|
|
18
|
+
import os
|
|
19
|
+
from abc import ABCMeta, abstractmethod
|
|
20
|
+
from statistics import mean
|
|
21
|
+
from typing import Dict
|
|
22
|
+
|
|
23
|
+
from model_analyzer.model_analyzer_exceptions import TritonModelAnalyzerException
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RecordType(ABCMeta):
|
|
27
|
+
"""
|
|
28
|
+
A metaclass that holds the instantiated Record types
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
record_types: Dict[str, "RecordType"] = {}
|
|
32
|
+
|
|
33
|
+
def __new__(cls, name, base, namespace):
|
|
34
|
+
"""
|
|
35
|
+
This function is called upon declaration of any classes of type
|
|
36
|
+
RecordType
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
record_type = super().__new__(cls, name, base, namespace)
|
|
40
|
+
|
|
41
|
+
# If record_type.tag is a string, register it here
|
|
42
|
+
if isinstance(record_type.tag, str):
|
|
43
|
+
cls.record_types[record_type.tag] = record_type
|
|
44
|
+
return record_type
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def get(cls, tag):
|
|
48
|
+
"""
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
tag : str
|
|
52
|
+
tag that a record type has registered it classname with
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
The class of type RecordType corresponding to the tag
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if tag not in cls.record_types:
|
|
60
|
+
try:
|
|
61
|
+
importlib.import_module("model_analyzer.record.types.%s" % tag)
|
|
62
|
+
except ImportError as e:
|
|
63
|
+
print(e)
|
|
64
|
+
return cls.record_types[tag]
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def get_all_record_types(cls):
|
|
68
|
+
"""
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
dict
|
|
72
|
+
keys are tags and values are
|
|
73
|
+
all the types that have this as a
|
|
74
|
+
metaclass
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
type_module_directory = os.path.join(
|
|
78
|
+
globals()["__spec__"].origin.rsplit("/", 1)[0], "types"
|
|
79
|
+
)
|
|
80
|
+
for filename in os.listdir(type_module_directory):
|
|
81
|
+
if filename != "__init__.py" and filename.endswith(".py"):
|
|
82
|
+
try:
|
|
83
|
+
importlib.import_module(
|
|
84
|
+
f"model_analyzer.record.types.{filename[:-3]}"
|
|
85
|
+
)
|
|
86
|
+
except AttributeError:
|
|
87
|
+
raise TritonModelAnalyzerException(
|
|
88
|
+
"Error retrieving all record types"
|
|
89
|
+
)
|
|
90
|
+
return cls.record_types
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Record(metaclass=RecordType):
|
|
94
|
+
"""
|
|
95
|
+
This class is used for representing
|
|
96
|
+
records
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, value, timestamp):
|
|
100
|
+
"""
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
value : float or int
|
|
104
|
+
The value of the GPU metrtic
|
|
105
|
+
timestamp : int
|
|
106
|
+
The timestamp for the record in nanoseconds
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
assert type(value) is float or type(value) is int
|
|
110
|
+
assert type(timestamp) is int
|
|
111
|
+
|
|
112
|
+
self._value = value
|
|
113
|
+
self._timestamp = timestamp
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def aggregation_function():
|
|
117
|
+
"""
|
|
118
|
+
The function that is used to aggregate
|
|
119
|
+
this type of record
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
callable()
|
|
124
|
+
[Records] -> Record
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
return lambda records: max(records, key=lambda r: r.value())
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def value_function():
|
|
131
|
+
"""
|
|
132
|
+
Returns the average value from a list
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
Average value of the list
|
|
137
|
+
"""
|
|
138
|
+
return mean
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def header(aggregation_tag=False):
|
|
143
|
+
"""
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
aggregation_tag : boolean
|
|
147
|
+
An optional tag that may be displayed as part of the header
|
|
148
|
+
indicating that this record has been aggregated using max, min or
|
|
149
|
+
average etc.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
str
|
|
154
|
+
The full name of the
|
|
155
|
+
metric.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
@abstractmethod
|
|
160
|
+
def tag(self):
|
|
161
|
+
"""
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
str
|
|
165
|
+
the name tag of the record type.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def to_dict(self):
|
|
169
|
+
return (self.tag, self.__dict__)
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def from_dict(cls, record_dict):
|
|
173
|
+
record = cls(0)
|
|
174
|
+
for key in ["_value", "_timestamp"]:
|
|
175
|
+
if key in record_dict:
|
|
176
|
+
setattr(record, key, record_dict[key])
|
|
177
|
+
return record
|
|
178
|
+
|
|
179
|
+
def value(self):
|
|
180
|
+
"""
|
|
181
|
+
This method returns the value of recorded metric
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
float
|
|
186
|
+
value of the metric
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
return self._value
|
|
190
|
+
|
|
191
|
+
def timestamp(self):
|
|
192
|
+
"""
|
|
193
|
+
This method should return the time at which the record was created.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
float
|
|
198
|
+
timestamp passed in during
|
|
199
|
+
record creation
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
return self._timestamp
|
|
203
|
+
|
|
204
|
+
def __mul__(self, other):
|
|
205
|
+
"""
|
|
206
|
+
Defines left multiplication for records with floats or ints.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
Record
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
if isinstance(other, (int, float)):
|
|
214
|
+
return self.__class__(value=(self.value() * other))
|
|
215
|
+
else:
|
|
216
|
+
raise TypeError
|
|
217
|
+
|
|
218
|
+
def __rmul__(self, other):
|
|
219
|
+
"""
|
|
220
|
+
Defines right multiplication
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
return self.__mul__(other)
|
|
224
|
+
|
|
225
|
+
def __truediv__(self, other):
|
|
226
|
+
"""
|
|
227
|
+
Defines left multiplication for records with floats or ints
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
Record
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
if isinstance(other, (int, float)):
|
|
235
|
+
return self.__class__(value=(self.value() / other))
|
|
236
|
+
|
|
237
|
+
else:
|
|
238
|
+
raise TypeError
|
|
239
|
+
|
|
240
|
+
@abstractmethod
|
|
241
|
+
def _positive_is_better(self) -> bool:
|
|
242
|
+
"""
|
|
243
|
+
Returns a bool indicating if a larger positive value is better
|
|
244
|
+
for a given record type
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
def calculate_percentage_gain(self, other: "Record") -> float:
|
|
248
|
+
"""
|
|
249
|
+
Calculates percentage gain between records
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
# When increasing values are better gain is based on the original value (other):
|
|
253
|
+
# example: 200 vs. 100 is (200 - 100) / 100 = 100%
|
|
254
|
+
# example: 100 vs. 200 is (100 - 200) / 200 = -50%
|
|
255
|
+
if self._positive_is_better():
|
|
256
|
+
return ((self.value() - other.value()) / other.value()) * 100
|
|
257
|
+
|
|
258
|
+
# When decreasing values are better gain is based on the new value (self):
|
|
259
|
+
# example: 100 vs. 200 is (200 - 100) / 100 = 100%
|
|
260
|
+
# example: 200 vs. 100 is (100 - 200) / 200 = -50%
|
|
261
|
+
else:
|
|
262
|
+
return ((other.value() - self.value()) / self.value()) * 100
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class IncreasingRecord(Record):
|
|
266
|
+
"""
|
|
267
|
+
Record where an increasing positive value is better
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
def _positive_is_better(self) -> bool:
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class DecreasingRecord(Record):
|
|
275
|
+
"""
|
|
276
|
+
Record where an increasing positive value is worse
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
def _positive_is_better(self) -> bool:
|
|
280
|
+
return False
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# 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, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
import itertools
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
|
|
20
|
+
from model_analyzer.model_analyzer_exceptions import TritonModelAnalyzerException
|
|
21
|
+
from model_analyzer.record.record import Record
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RecordAggregator:
|
|
25
|
+
"""
|
|
26
|
+
Stores a collection of Record objects.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self._records = defaultdict(list)
|
|
31
|
+
|
|
32
|
+
def insert(self, record):
|
|
33
|
+
"""
|
|
34
|
+
Insert a record into the RecordAggregator
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
record : Record
|
|
39
|
+
A record to be inserted
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
if isinstance(record, Record):
|
|
43
|
+
record_type = type(record)
|
|
44
|
+
self._records[record_type].append(record)
|
|
45
|
+
else:
|
|
46
|
+
raise TritonModelAnalyzerException(
|
|
47
|
+
"Can only add objects of type 'Record' to RecordAggregator"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def insert_all(self, record_list):
|
|
51
|
+
"""
|
|
52
|
+
Insert records from a list of records
|
|
53
|
+
into the RecordAggregator
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
record_list : List of Records
|
|
58
|
+
The records to insert
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
for record in record_list:
|
|
62
|
+
self.insert(record)
|
|
63
|
+
|
|
64
|
+
def add_key(self, record_type, records):
|
|
65
|
+
"""
|
|
66
|
+
Adds or replaces all the records of a given record_type with the new
|
|
67
|
+
records
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
record_type : Record
|
|
72
|
+
record_type to add to the records.
|
|
73
|
+
records : list
|
|
74
|
+
List of new records to be added.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
self._records[record_type] = records
|
|
78
|
+
|
|
79
|
+
def filter_records(self, record_types=None, filters=None):
|
|
80
|
+
"""
|
|
81
|
+
Get records that satisfy the given list of criteria.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
|
|
86
|
+
record_types : list of types of Records
|
|
87
|
+
the types of the records we are
|
|
88
|
+
imposing the filter criteria on.
|
|
89
|
+
|
|
90
|
+
filters : list of callables
|
|
91
|
+
conditions that determine whether
|
|
92
|
+
a given record should be returned.
|
|
93
|
+
If no filters specified, all records
|
|
94
|
+
of types specified by record_types will be
|
|
95
|
+
returned.
|
|
96
|
+
Note : This must be of the same length
|
|
97
|
+
as the list of record_types, or omitted.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
RecordAggregator
|
|
102
|
+
Returns a new RecordAggregator containing the filtered
|
|
103
|
+
records
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
filtered_records = RecordAggregator()
|
|
107
|
+
if not record_types and not filters:
|
|
108
|
+
for record_type, records in self._records.items():
|
|
109
|
+
filtered_records.add_key(record_type, records)
|
|
110
|
+
return filtered_records
|
|
111
|
+
|
|
112
|
+
if record_types and not filters:
|
|
113
|
+
try:
|
|
114
|
+
for record_type in record_types:
|
|
115
|
+
filtered_records.add_key(record_type, self._records[record_type])
|
|
116
|
+
return filtered_records
|
|
117
|
+
except KeyError as k:
|
|
118
|
+
raise TritonModelAnalyzerException(
|
|
119
|
+
f"Record type '{k.header()}' not found in this RecordAggregator"
|
|
120
|
+
)
|
|
121
|
+
if filters and not record_types:
|
|
122
|
+
raise TritonModelAnalyzerException(
|
|
123
|
+
"Must specify the record types corresponding to each filter criterion."
|
|
124
|
+
)
|
|
125
|
+
if len(record_types) != len(filters):
|
|
126
|
+
raise TritonModelAnalyzerException(
|
|
127
|
+
"Must specify the same number of record types as filter criteria."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Remove records that do not satisfy criteria
|
|
131
|
+
for h, f in zip(record_types, filters):
|
|
132
|
+
for record in self._records[h]:
|
|
133
|
+
if f(record):
|
|
134
|
+
filtered_records.insert(record)
|
|
135
|
+
|
|
136
|
+
return filtered_records
|
|
137
|
+
|
|
138
|
+
def groupby(self, record_types, groupby_criterion):
|
|
139
|
+
"""
|
|
140
|
+
Group all the records of a certain type together if they have the
|
|
141
|
+
same value for a given groupbby criteria.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
record_types : list
|
|
146
|
+
A list of record type
|
|
147
|
+
groupby_criterion : callable
|
|
148
|
+
This callable will receive a single record as the argument and
|
|
149
|
+
must return the value that will be used for groupby
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
dict
|
|
154
|
+
A dictionary of dictionaries where the first level keys are the
|
|
155
|
+
record type and the second level keys are unique values returned
|
|
156
|
+
by groupby_criteria and the values are the aggregated records.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
field_values = {
|
|
160
|
+
record_type: set(
|
|
161
|
+
[groupby_criterion(record) for record in self._records[record_type]]
|
|
162
|
+
)
|
|
163
|
+
for record_type in record_types
|
|
164
|
+
}
|
|
165
|
+
groupby_result = defaultdict(list)
|
|
166
|
+
for record_type in record_types:
|
|
167
|
+
groupby_result[record_type] = defaultdict(list)
|
|
168
|
+
for field_value in field_values[record_type]:
|
|
169
|
+
aggregated_result = self.filter_records(
|
|
170
|
+
record_types=[record_type],
|
|
171
|
+
filters=[lambda r: groupby_criterion(r) == field_value],
|
|
172
|
+
).aggregate(record_types=[record_type])
|
|
173
|
+
groupby_result[record_type][field_value] = aggregated_result[
|
|
174
|
+
record_type
|
|
175
|
+
]
|
|
176
|
+
return groupby_result
|
|
177
|
+
|
|
178
|
+
def record_types(self):
|
|
179
|
+
"""
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
list of str
|
|
183
|
+
a list of the types of records in this
|
|
184
|
+
RecordAgrregator
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
return list(self._records)
|
|
188
|
+
|
|
189
|
+
def total(self, record_type=None):
|
|
190
|
+
"""
|
|
191
|
+
Get the total number of records in
|
|
192
|
+
the RecordAggregator
|
|
193
|
+
|
|
194
|
+
Parameters
|
|
195
|
+
----------
|
|
196
|
+
record_type : a class name of type Record
|
|
197
|
+
The type of records to count,
|
|
198
|
+
if None, count all types
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
int
|
|
203
|
+
number of records in
|
|
204
|
+
the RecordAggregator
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
if record_type:
|
|
208
|
+
if record_type not in self._records:
|
|
209
|
+
raise TritonModelAnalyzerException(
|
|
210
|
+
f"Record type '{record_type.header()}' not found in this RecordAggregator"
|
|
211
|
+
)
|
|
212
|
+
return len(self._records[record_type])
|
|
213
|
+
return sum(len(self._records[k]) for k in self._records)
|
|
214
|
+
|
|
215
|
+
def aggregate(self, record_types=None):
|
|
216
|
+
"""
|
|
217
|
+
Parameters
|
|
218
|
+
----------
|
|
219
|
+
record_types : List of Record types
|
|
220
|
+
The type of records to aggregate.
|
|
221
|
+
If None, aggregates all records
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
dict
|
|
226
|
+
keys are requested record types
|
|
227
|
+
and values are the aggregated values
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
if not record_types:
|
|
231
|
+
record_types = self.record_types()
|
|
232
|
+
aggregated_records = {
|
|
233
|
+
record_type: record_type.aggregation_function()(self._records[record_type])
|
|
234
|
+
for record_type in record_types
|
|
235
|
+
}
|
|
236
|
+
return aggregated_records
|
|
237
|
+
|
|
238
|
+
def get_records(self):
|
|
239
|
+
"""
|
|
240
|
+
Get all the records.
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
dict
|
|
245
|
+
A dictionary where the keys are record types and the values are
|
|
246
|
+
an array of records with the specified type
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
return self._records
|
|
250
|
+
|
|
251
|
+
def _flatten_records(self, records):
|
|
252
|
+
"""
|
|
253
|
+
Flatten the records array by joining all the arrays together.
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
return list(itertools.chain.from_iterable(records))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# 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, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# 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, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
from functools import total_ordering
|
|
18
|
+
|
|
19
|
+
from model_analyzer.record.record import IncreasingRecord
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@total_ordering
|
|
23
|
+
class CPUAvailableRAM(IncreasingRecord):
|
|
24
|
+
"""
|
|
25
|
+
The Available CPU memory
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
tag = "cpu_available_ram"
|
|
29
|
+
|
|
30
|
+
def __init__(self, value, timestamp=0):
|
|
31
|
+
"""
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
value : float
|
|
35
|
+
CPU free memory
|
|
36
|
+
timestamp : int
|
|
37
|
+
The timestamp for the record in nanoseconds
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
super().__init__(value, timestamp)
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def header(aggregation_tag=False):
|
|
44
|
+
"""
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
aggregation_tag: bool
|
|
48
|
+
An optional tag that may be displayed
|
|
49
|
+
as part of the header indicating that
|
|
50
|
+
this record has been aggregated using
|
|
51
|
+
max, min or average etc.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
str
|
|
56
|
+
The full name of the
|
|
57
|
+
metric.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
return ("Max " if aggregation_tag else "") + "RAM Available (MB)"
|
|
61
|
+
|
|
62
|
+
def __eq__(self, other):
|
|
63
|
+
"""
|
|
64
|
+
Allows checking for
|
|
65
|
+
equality between two records
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
return self.value() == other.value()
|
|
69
|
+
|
|
70
|
+
def __lt__(self, other):
|
|
71
|
+
"""
|
|
72
|
+
Allows checking if
|
|
73
|
+
this record is less than
|
|
74
|
+
the other
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
return self.value() < other.value()
|
|
78
|
+
|
|
79
|
+
def __add__(self, other):
|
|
80
|
+
"""
|
|
81
|
+
Allows adding two records together
|
|
82
|
+
to produce a brand new record.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
return CPUAvailableRAM(value=(self.value() + other.value()))
|
|
86
|
+
|
|
87
|
+
def __sub__(self, other):
|
|
88
|
+
"""
|
|
89
|
+
Allows subtracting two records together
|
|
90
|
+
to produce a brand new record.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
return CPUAvailableRAM(value=(self.value() - other.value()))
|