Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle missing data in console and csv exporter #201

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions genai-perf/genai_perf/export_data/console_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,13 @@ def _construct_table(self, table: Table) -> None:
metric_str += f" ({metric.unit})" if metric.unit != "tokens" else ""
row_values = [metric_str]
for stat in self.STAT_COLUMN_KEYS:
value = self._stats[metric.name][stat]
row_values.append(f"{value:,.2f}")
value = self._stats[metric.name].get(stat, None)
row_values.append(f"{value:,.2f}" if value else "N/A")

table.add_row(*row_values)

for metric in self._metrics.system_metrics:
metric_str = metric.name.replace("_", " ").capitalize()
# metric_str = metric_str.replace("throughput", "tput")
if metric.name == "request_goodput":
if not self._args.goodput:
continue
Expand Down
4 changes: 2 additions & 2 deletions genai-perf/genai_perf/export_data/csv_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ def _write_request_metrics(self, csv_writer) -> None:
metric_str += f" ({metric.unit})" if metric.unit != "tokens" else ""
row_values = [metric_str]
for stat in self.REQUEST_METRICS_HEADER[1:]:
value = self._stats[metric.name][stat]
row_values.append(f"{value:,.2f}")
value = self._stats[metric.name].get(stat, None)
row_values.append(f"{value:,.2f}" if value else "N/A")

csv_writer.writerow(row_values)

Expand Down
55 changes: 55 additions & 0 deletions genai-perf/tests/test_console_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,58 @@ def test_valid_telemetry_verbose(self, monkeypatch, capsys) -> None:

returned_data = capsys.readouterr().out
assert returned_data == expected_content

def test_missing_data(self, monkeypatch, capsys) -> None:
argv = [
"genai-perf",
"profile",
"-m",
"model_name",
"--service-kind",
"openai",
"--endpoint-type",
"chat",
]
monkeypatch.setattr("sys.argv", argv)
args, _ = parser.parse_args()

metrics = LLMMetrics(
request_throughputs=[123],
request_latencies=[4, 5, 6],
time_to_first_tokens=[4, 5, 6], # same as request_latency
inter_token_latencies=[], # no ITL
output_token_throughputs=[456],
output_sequence_lengths=[1, 2, 3],
input_sequence_lengths=[5, 6, 7],
)
stats = Statistics(metrics=metrics)

config = ExporterConfig()
config.stats = stats.stats_dict
config.metrics = stats.metrics
config.args = args

# Missing data
del config.stats["request_latency"]["avg"]
del config.stats["output_sequence_length"]["max"]
del config.stats["input_sequence_length"]

exporter = ConsoleExporter(config)
exporter.export()

# No TTFT and ITL in the output
expected_content = (
" NVIDIA GenAI-Perf | LLM Metrics \n"
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓\n"
"┃ Statistic ┃ avg ┃ min ┃ max ┃ p99 ┃ p90 ┃ p75 ┃\n"
"┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━╇━━━━━━┩\n"
"│ Request latency (ms) │ N/A │ 4.00 │ 6.00 │ 5.98 │ 5.80 │ 5.50 │\n"
"│ Output sequence length │ 2.00 │ 1.00 │ N/A │ 2.98 │ 2.80 │ 2.50 │\n"
"│ Input sequence length │ N/A │ N/A │ N/A │ N/A │ N/A │ N/A │\n"
"│ Output token throughput (per sec) │ 456.… │ N/A │ N/A │ N/A │ N/A │ N/A │\n"
"│ Request throughput (per sec) │ 123.… │ N/A │ N/A │ N/A │ N/A │ N/A │\n"
"└───────────────────────────────────┴───────┴──────┴──────┴──────┴──────┴──────┘\n"
)

returned_data = capsys.readouterr().out
assert returned_data == expected_content
60 changes: 56 additions & 4 deletions genai-perf/tests/test_csv_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,6 @@ def test_nonstreaming_llm_csv_output(
Collect LLM metrics from profile export data and confirm correct values are
printed in csv.
"""
artifacts_dir = "artifacts/model_name-openai-chat-concurrency1"
custom_filename = "custom_export.json"
expected_filename = f"custom_export_genai_perf.csv"
argv = [
"genai-perf",
"profile",
Expand All @@ -153,7 +150,7 @@ def test_nonstreaming_llm_csv_output(
"--endpoint-type",
"chat",
"--profile-export-file",
custom_filename,
"custom_export.json",
]
monkeypatch.setattr("sys.argv", argv)
args, _ = parser.parse_args()
Expand All @@ -169,6 +166,7 @@ def test_nonstreaming_llm_csv_output(
exporter = CsvExporter(config)
exporter.export()

expected_filename = f"custom_export_genai_perf.csv"
expected_content = [
"Metric,avg,min,max,p99,p95,p90,p75,p50,p25\r\n",
"Request Latency (ms),5.00,4.00,6.00,5.98,5.90,5.80,5.50,5.00,4.50\r\n",
Expand Down Expand Up @@ -397,3 +395,57 @@ def test_triton_telemetry_output(
]

assert returned_data == expected_content

def test_missing_data(
self, monkeypatch, mock_read_write: pytest.MonkeyPatch, llm_metrics: LLMMetrics
) -> None:
"""
Test if missing data does not throw an error and are marked as "N/A".
"""
argv = [
"genai-perf",
"profile",
"-m",
"model_name",
"--service-kind",
"openai",
"--endpoint-type",
"chat",
"--profile-export-file",
"custom_export.json",
]
monkeypatch.setattr("sys.argv", argv)
args, _ = parser.parse_args()

stats = Statistics(metrics=llm_metrics)

config = ExporterConfig()
config.stats = stats.stats_dict
config.metrics = stats.metrics
config.artifact_dir = Path(".")
config.args = args

# Missing data
del config.stats["request_latency"]["avg"]
del config.stats["output_sequence_length"]["max"]
del config.stats["input_sequence_length"]

exporter = CsvExporter(config)
exporter.export()

expected_filename = f"custom_export_genai_perf.csv"
expected_content = [
"Metric,avg,min,max,p99,p95,p90,p75,p50,p25\r\n",
"Request Latency (ms),N/A,4.00,6.00,5.98,5.90,5.80,5.50,5.00,4.50\r\n",
"Output Sequence Length,2.00,1.00,N/A,2.98,2.90,2.80,2.50,2.00,1.50\r\n",
"Input Sequence Length,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A\r\n",
"\r\n",
"Metric,Value\r\n",
"Output Token Throughput (per sec),456.00\r\n",
"Request Throughput (per sec),123.00\r\n",
]
returned_data = [
data for filename, data in mock_read_write if filename == expected_filename
]

assert returned_data == expected_content
2 changes: 1 addition & 1 deletion src/client_backend/openai/http_client.h
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, pre-commit was complaining about the order of the includes so I had to change it.

Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <string>
#include <thread>

namespace triton { namespace perfanalyzer { namespace clientbackend {
namespace openai {
Expand Down
Loading