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,42 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2022-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
|
+
|
|
18
|
+
def truncate_model_config_name(model_config_name):
|
|
19
|
+
"""
|
|
20
|
+
Truncates the model configuration name if its length exceeds the threshold length.
|
|
21
|
+
ex: long_model_name_config_4 --> long_mod..._config_4
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
model_config_name: string
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
string
|
|
28
|
+
The truncated model configuration name,
|
|
29
|
+
or the original name if it is shorter than the threshold length.
|
|
30
|
+
"""
|
|
31
|
+
max_model_config_name_len = 35
|
|
32
|
+
|
|
33
|
+
if len(model_config_name) > max_model_config_name_len:
|
|
34
|
+
config_name = model_config_name[model_config_name.rfind("config_") :]
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
model_config_name[: (max_model_config_name_len - len(config_name) - 3)]
|
|
38
|
+
+ "..."
|
|
39
|
+
+ config_name
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return model_config_name
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2021-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,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2021-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 typing import TYPE_CHECKING, Dict, Union
|
|
18
|
+
|
|
19
|
+
from model_analyzer.record.record import Record
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from model_analyzer.result.run_config_measurement import RunConfigMeasurement
|
|
23
|
+
|
|
24
|
+
from model_analyzer.config.input.config_command_profile import ConfigCommandProfile
|
|
25
|
+
from model_analyzer.config.input.config_command_report import ConfigCommandReport
|
|
26
|
+
from model_analyzer.constants import GLOBAL_CONSTRAINTS_KEY
|
|
27
|
+
from model_analyzer.result.model_constraints import ModelConstraints
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ConstraintManager:
|
|
31
|
+
"""
|
|
32
|
+
Handles processing and applying
|
|
33
|
+
constraints on a given measurements
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
config: ConfigCommandProfile or ConfigCommandReport
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self, config: Union[ConfigCommandProfile, ConfigCommandReport]
|
|
42
|
+
) -> None:
|
|
43
|
+
self._constraints = {}
|
|
44
|
+
|
|
45
|
+
if config:
|
|
46
|
+
# Model constraints
|
|
47
|
+
if "profile_models" in config.get_config():
|
|
48
|
+
for model in config.profile_models:
|
|
49
|
+
self._constraints[model.model_name()] = model.constraints()
|
|
50
|
+
|
|
51
|
+
# Global constraints
|
|
52
|
+
if "constraints" in config.get_all_config():
|
|
53
|
+
self._constraints[GLOBAL_CONSTRAINTS_KEY] = ModelConstraints(
|
|
54
|
+
config.get_all_config()["constraints"]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def get_constraints_for_all_models(self):
|
|
58
|
+
"""
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
dict
|
|
62
|
+
keys are model names, and values are ModelConstraints objects
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
return self._constraints
|
|
66
|
+
|
|
67
|
+
def satisfies_constraints(
|
|
68
|
+
self, run_config_measurement: "RunConfigMeasurement"
|
|
69
|
+
) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Checks that the measurements, for every model, satisfy
|
|
72
|
+
the provided list of constraints
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
run_config_measurement : RunConfigMeasurement
|
|
77
|
+
The measurement to check against the constraints
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
True if measurement passes constraints
|
|
82
|
+
False otherwise
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
if self._constraints:
|
|
86
|
+
for model_name, model_metrics in run_config_measurement.data().items():
|
|
87
|
+
for metric in model_metrics:
|
|
88
|
+
if self._metric_matches_constraint(
|
|
89
|
+
metric, self._constraints[model_name]
|
|
90
|
+
):
|
|
91
|
+
if (
|
|
92
|
+
self._get_failure_percentage(
|
|
93
|
+
metric, self._constraints[model_name][metric.tag]
|
|
94
|
+
)
|
|
95
|
+
> 0
|
|
96
|
+
):
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def constraint_failure_percentage(
|
|
102
|
+
self, run_config_measurement: "RunConfigMeasurement"
|
|
103
|
+
) -> float:
|
|
104
|
+
"""
|
|
105
|
+
Additive percentage, for every measurement, in every model, of how much
|
|
106
|
+
the RCM is failing the constraints by
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
float
|
|
111
|
+
"""
|
|
112
|
+
failure_percentage: float = 0
|
|
113
|
+
|
|
114
|
+
if self._constraints:
|
|
115
|
+
for model_name, model_metrics in run_config_measurement.data().items():
|
|
116
|
+
for metric in model_metrics:
|
|
117
|
+
if self._metric_matches_constraint(
|
|
118
|
+
metric, self._constraints[model_name]
|
|
119
|
+
):
|
|
120
|
+
failure_percentage += self._get_failure_percentage(
|
|
121
|
+
metric, self._constraints[model_name][metric.tag]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return failure_percentage * 100
|
|
125
|
+
|
|
126
|
+
def _metric_matches_constraint(
|
|
127
|
+
self, metric: Record, constraint: ModelConstraints
|
|
128
|
+
) -> bool:
|
|
129
|
+
if constraint.has_metric(metric.tag):
|
|
130
|
+
return True
|
|
131
|
+
else:
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def _get_failure_percentage(
|
|
135
|
+
self, metric: Record, constraint: Dict[str, int]
|
|
136
|
+
) -> float:
|
|
137
|
+
failure_percentage = 0
|
|
138
|
+
|
|
139
|
+
if "min" in constraint:
|
|
140
|
+
if metric.value() < constraint["min"]:
|
|
141
|
+
failure_percentage = (constraint["min"] - metric.value()) / constraint[
|
|
142
|
+
"min"
|
|
143
|
+
]
|
|
144
|
+
if "max" in constraint:
|
|
145
|
+
if metric.value() > constraint["max"]:
|
|
146
|
+
failure_percentage = (metric.value() - constraint["max"]) / constraint[
|
|
147
|
+
"max"
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
return failure_percentage
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2022-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 logging
|
|
18
|
+
from copy import deepcopy
|
|
19
|
+
from functools import total_ordering
|
|
20
|
+
from statistics import mean
|
|
21
|
+
|
|
22
|
+
from model_analyzer.constants import COMPARISON_SCORE_THRESHOLD, LOGGER_NAME
|
|
23
|
+
from model_analyzer.record.record import RecordType
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(LOGGER_NAME)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@total_ordering
|
|
29
|
+
class ModelConfigMeasurement:
|
|
30
|
+
"""
|
|
31
|
+
Encapsulates the set of non-gpu metrics obtained from a single model config's
|
|
32
|
+
RunConfig run
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, model_config_name, model_specific_pa_params, non_gpu_data):
|
|
36
|
+
"""
|
|
37
|
+
model_config_name : string
|
|
38
|
+
The model config name that was used in the RunConfig
|
|
39
|
+
model_specific_pa_params: dict
|
|
40
|
+
Dictionary of PA parameters that can change between models
|
|
41
|
+
in a multi-model RunConfig
|
|
42
|
+
non_gpu_data : list of Records
|
|
43
|
+
Metrics that do not have a GPU UUID associated with them,
|
|
44
|
+
from either CPU or PA
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
self._model_config_name = model_config_name
|
|
48
|
+
self._model_specific_pa_params = model_specific_pa_params
|
|
49
|
+
self._non_gpu_data = non_gpu_data
|
|
50
|
+
|
|
51
|
+
self._non_gpu_data_from_tag = self._get_non_gpu_data_from_tag()
|
|
52
|
+
|
|
53
|
+
# Set a default metric weighting
|
|
54
|
+
self._metric_weights = {"perf_throughput": 1}
|
|
55
|
+
|
|
56
|
+
def to_dict(self):
|
|
57
|
+
mcm_dict = deepcopy(self.__dict__)
|
|
58
|
+
del mcm_dict["_metric_weights"]
|
|
59
|
+
|
|
60
|
+
return mcm_dict
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def from_dict(cls, model_config_measurement_dict):
|
|
64
|
+
model_config_measurement = ModelConfigMeasurement(None, {}, [])
|
|
65
|
+
|
|
66
|
+
model_config_measurement._model_config_name = model_config_measurement_dict[
|
|
67
|
+
"_model_config_name"
|
|
68
|
+
]
|
|
69
|
+
model_config_measurement._model_specific_pa_params = (
|
|
70
|
+
model_config_measurement_dict["_model_specific_pa_params"]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
model_config_measurement._non_gpu_data = cls._deserialize_non_gpu_data(
|
|
74
|
+
model_config_measurement, model_config_measurement_dict["_non_gpu_data"]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
model_config_measurement._non_gpu_data_from_tag = (
|
|
78
|
+
cls._get_non_gpu_data_from_tag(model_config_measurement)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return model_config_measurement
|
|
82
|
+
|
|
83
|
+
def set_metric_weighting(self, metric_objectives):
|
|
84
|
+
"""
|
|
85
|
+
Sets the metric weighting for this measurement based
|
|
86
|
+
on the objectives
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
metric_objectives : dict of RecordTypes
|
|
91
|
+
keys are the metric types, and values are the relative importance
|
|
92
|
+
of the keys with respect to each other
|
|
93
|
+
"""
|
|
94
|
+
self._metric_weights = {
|
|
95
|
+
objective: (value / sum(metric_objectives.values()))
|
|
96
|
+
for objective, value in metric_objectives.items()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def model_config_name(self):
|
|
100
|
+
"""
|
|
101
|
+
Return the model_config name
|
|
102
|
+
used to get this measurement
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
return self._model_config_name
|
|
106
|
+
|
|
107
|
+
def model_name(self):
|
|
108
|
+
"""
|
|
109
|
+
Return the model name
|
|
110
|
+
|
|
111
|
+
TODO: This method should be replaced with the extract_model_name_from_variant_name() static method,
|
|
112
|
+
once the ensemble code gets merged to main
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
return self._model_config_name.partition("_config_")[0]
|
|
116
|
+
|
|
117
|
+
def model_specific_pa_params(self):
|
|
118
|
+
"""
|
|
119
|
+
Return a dict of model specific PA parameters
|
|
120
|
+
used in this measurement
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
return self._model_specific_pa_params
|
|
124
|
+
|
|
125
|
+
def non_gpu_data(self):
|
|
126
|
+
"""
|
|
127
|
+
Return a list of the non-GPU specific
|
|
128
|
+
measurement Records
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
return self._non_gpu_data
|
|
132
|
+
|
|
133
|
+
def get_metric(self, tag):
|
|
134
|
+
"""
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
tag : str
|
|
138
|
+
A human readable tag that corresponds
|
|
139
|
+
to a particular metric
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
Record
|
|
144
|
+
metric Record corresponding to
|
|
145
|
+
the tag, in this measurement, None
|
|
146
|
+
if tag not found.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
if tag in self._non_gpu_data_from_tag:
|
|
150
|
+
return self._non_gpu_data_from_tag[tag]
|
|
151
|
+
else:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def get_metric_value(self, tag, default_value=0):
|
|
155
|
+
"""
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
tag : str
|
|
159
|
+
A human readable tag that corresponds
|
|
160
|
+
to a particular metric
|
|
161
|
+
default_value : any
|
|
162
|
+
Value to return if tag is not found
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
Record
|
|
167
|
+
Value of the metric Record corresponding
|
|
168
|
+
to the tag, in this measurement,
|
|
169
|
+
default_value if tag not found.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
metric = self.get_metric(tag)
|
|
173
|
+
if metric is None:
|
|
174
|
+
return default_value
|
|
175
|
+
return metric.value()
|
|
176
|
+
|
|
177
|
+
def get_weighted_score(self, other):
|
|
178
|
+
"""
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
other: ModelConfigMeasurement
|
|
182
|
+
set of (non_gpu) metrics to be compared against
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
float
|
|
187
|
+
The weighted score between this ModelConfig
|
|
188
|
+
and the other ModelConfig
|
|
189
|
+
"""
|
|
190
|
+
return self._calculate_weighted_score(other)
|
|
191
|
+
|
|
192
|
+
def is_better_than(self, other):
|
|
193
|
+
"""
|
|
194
|
+
Checks whether a measurement is better than
|
|
195
|
+
another
|
|
196
|
+
|
|
197
|
+
If True, this means this measurement is better
|
|
198
|
+
than the other.
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
other: ModelConfigMeasurement
|
|
203
|
+
set of (non_gpu) metrics to be compared against
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
return self._compare_measurements(other) == 1
|
|
207
|
+
|
|
208
|
+
def __eq__(self, other):
|
|
209
|
+
"""
|
|
210
|
+
Check whether two sets of measurements are equivalent
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
other: ModelConfigMeasurement
|
|
215
|
+
set of (non_gpu) metrics to be compared against
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
return self._compare_measurements(other) == 0
|
|
219
|
+
|
|
220
|
+
def __lt__(self, other):
|
|
221
|
+
"""
|
|
222
|
+
Checks whether a measurement is better than
|
|
223
|
+
another
|
|
224
|
+
|
|
225
|
+
This is used when sorting
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
other: ModelConfigMeasurement
|
|
230
|
+
set of (non_gpu) metrics to be compared against
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
bool:
|
|
235
|
+
True if other is better than or equal to self
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
return not self.is_better_than(other)
|
|
239
|
+
|
|
240
|
+
def _compare_measurements(self, other):
|
|
241
|
+
"""
|
|
242
|
+
Compares two ModelConfig measurements
|
|
243
|
+
based on the weighted metric objectives
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
other : ModelConfigMeasurement
|
|
248
|
+
set of (non_gpu) metrics to be compared against
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
int
|
|
253
|
+
0
|
|
254
|
+
if the results are determined
|
|
255
|
+
to be the same within a threshold
|
|
256
|
+
1
|
|
257
|
+
if self > other (is better than)
|
|
258
|
+
-1
|
|
259
|
+
if self < other (is worse than)
|
|
260
|
+
"""
|
|
261
|
+
weighted_score = self._calculate_weighted_score(other)
|
|
262
|
+
|
|
263
|
+
if weighted_score > COMPARISON_SCORE_THRESHOLD:
|
|
264
|
+
return 1
|
|
265
|
+
elif weighted_score < -COMPARISON_SCORE_THRESHOLD:
|
|
266
|
+
return -1
|
|
267
|
+
return 0
|
|
268
|
+
|
|
269
|
+
def _calculate_weighted_score(self, other):
|
|
270
|
+
"""
|
|
271
|
+
Calculates the weighted score between two
|
|
272
|
+
ModelConfig measurements based on the weighted
|
|
273
|
+
metric objectives
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
other : ModelConfigMeasurement
|
|
278
|
+
set of (non_gpu) metrics to be compared against
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
float
|
|
283
|
+
The weighted score. A positive value indicates
|
|
284
|
+
this ModelConfig measurement is better than the other
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
weighted_score = 0.0
|
|
288
|
+
for objective, weight in self._metric_weights.items():
|
|
289
|
+
self_metric = self.get_metric(tag=objective)
|
|
290
|
+
other_metric = other.get_metric(tag=objective)
|
|
291
|
+
|
|
292
|
+
# Handle the case where objective GPU metric is queried on CPU only
|
|
293
|
+
if self_metric and other_metric is None:
|
|
294
|
+
return 1
|
|
295
|
+
elif other_metric and self_metric is None:
|
|
296
|
+
return -1
|
|
297
|
+
elif self_metric is None and other_metric is None:
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
metric_diff = self_metric - other_metric
|
|
301
|
+
average = mean([self_metric.value(), other_metric.value()])
|
|
302
|
+
weighted_score += weight * (metric_diff.value() / average)
|
|
303
|
+
|
|
304
|
+
return weighted_score
|
|
305
|
+
|
|
306
|
+
def calculate_weighted_percentage_gain(self, other):
|
|
307
|
+
"""
|
|
308
|
+
Calculates the weighted percentage between two
|
|
309
|
+
ModelConfig measurements based on the weighted
|
|
310
|
+
metric objectives
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
other : ModelConfigMeasurement
|
|
315
|
+
set of (non_gpu) metrics to be compared against
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
float
|
|
320
|
+
The weighted percentage gain. A positive value indicates
|
|
321
|
+
this ModelConfig measurement is better than the other
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
weighted_pct = 0.0
|
|
325
|
+
for objective, weight in self._metric_weights.items():
|
|
326
|
+
self_metric = self.get_metric(tag=objective)
|
|
327
|
+
other_metric = other.get_metric(tag=objective)
|
|
328
|
+
|
|
329
|
+
# Handle the case where objective GPU metric is queried on CPU only
|
|
330
|
+
if self_metric and other_metric is None:
|
|
331
|
+
return 100
|
|
332
|
+
elif other_metric and self_metric is None:
|
|
333
|
+
return -100
|
|
334
|
+
elif self_metric is None and other_metric is None:
|
|
335
|
+
return 0
|
|
336
|
+
|
|
337
|
+
metric_pct = self_metric.calculate_percentage_gain(other_metric)
|
|
338
|
+
|
|
339
|
+
weighted_pct += metric_pct * weight
|
|
340
|
+
|
|
341
|
+
return weighted_pct
|
|
342
|
+
|
|
343
|
+
def _get_non_gpu_data_from_tag(self):
|
|
344
|
+
return {metric.tag: metric for metric in self._non_gpu_data}
|
|
345
|
+
|
|
346
|
+
def _deserialize_non_gpu_data(self, serialized_non_gpu_data):
|
|
347
|
+
non_gpu_data = []
|
|
348
|
+
|
|
349
|
+
for [tag, record_dict] in serialized_non_gpu_data:
|
|
350
|
+
record_type = RecordType.get(tag)
|
|
351
|
+
record = record_type.from_dict(record_dict)
|
|
352
|
+
non_gpu_data.append(record)
|
|
353
|
+
|
|
354
|
+
return non_gpu_data
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright 2021-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
|
+
|
|
18
|
+
class ModelConstraints:
|
|
19
|
+
"""
|
|
20
|
+
A class representing the Constraints used for a single model.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, constraints):
|
|
24
|
+
"""
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
constraints: dict
|
|
28
|
+
keys are strings and
|
|
29
|
+
values are dict of single str: int pair
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
self._constraints = {}
|
|
33
|
+
if constraints:
|
|
34
|
+
self._constraints = constraints
|
|
35
|
+
|
|
36
|
+
def to_dict(self):
|
|
37
|
+
"""
|
|
38
|
+
Returns constraints dictionary
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
----------
|
|
42
|
+
constraints: dict
|
|
43
|
+
"""
|
|
44
|
+
return self._constraints
|
|
45
|
+
|
|
46
|
+
def has_metric(self, name):
|
|
47
|
+
"""
|
|
48
|
+
To check if given metric tag is present in constraints
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
----------
|
|
52
|
+
bool:
|
|
53
|
+
True if metric is present in constraints else False
|
|
54
|
+
"""
|
|
55
|
+
if name and name in self._constraints:
|
|
56
|
+
return True
|
|
57
|
+
else:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
def __getitem__(self, name):
|
|
61
|
+
"""
|
|
62
|
+
To subscript constraints using metric name
|
|
63
|
+
ex: model_constraints['perf_latency_p99']
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
name: str
|
|
68
|
+
metric name
|
|
69
|
+
"""
|
|
70
|
+
if name in self._constraints:
|
|
71
|
+
return self._constraints[name]
|
|
72
|
+
else:
|
|
73
|
+
msg = f"'{name}' key not found in constraints"
|
|
74
|
+
raise KeyError(msg)
|
|
75
|
+
|
|
76
|
+
def __bool__(self):
|
|
77
|
+
"""
|
|
78
|
+
To check if constraints are empty
|
|
79
|
+
"""
|
|
80
|
+
if self._constraints:
|
|
81
|
+
return True
|
|
82
|
+
else:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
def __eq__(self, other):
|
|
86
|
+
"""
|
|
87
|
+
To compare two ModelConstraints objects
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
other: ModelConstraints object
|
|
92
|
+
"""
|
|
93
|
+
if self._constraints == other._constraints:
|
|
94
|
+
return True
|
|
95
|
+
else:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
def items(self):
|
|
99
|
+
return self._constraints.items()
|
|
100
|
+
|
|
101
|
+
def __repr__(self):
|
|
102
|
+
return str(self._constraints)
|
|
103
|
+
|
|
104
|
+
def __iter__(self):
|
|
105
|
+
return iter(self._constraints)
|