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

[bench] Add code to run multiple command lines and export the result in a csv file #1641

Merged
merged 17 commits into from
Jul 3, 2024
6 changes: 6 additions & 0 deletions onnxscript/tools/benchmark/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
from onnxscript.tools.benchmark.benchmark_helpers import (
common_export,
get_parsed_args,
make_configs,
make_dataframe_from_benchmark_data,
multi_run,
run_inference,
run_onnx_inference,
)

__all__ = [
"get_parsed_args",
"common_export",
"make_configs",
"multi_run",
"make_dataframe_from_benchmark_data",
"run_inference",
"run_onnx_inference",
]
26 changes: 26 additions & 0 deletions onnxscript/tools/benchmark/benchmark_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import argparse
import itertools
import multiprocessing
import os
import platform
Expand Down Expand Up @@ -697,3 +698,28 @@
print(f"[run_inference] measure done in {time.perf_counter() - begin}")

return stats


def multi_run(kwargs: dict[str, Any]) -> bool:
"""Checks if multiple values were sent for one argument."""
return any(isinstance(v, str) and "," in v for v in kwargs.values())


def make_configs(kwargs: dict[str, Any]) -> list[dict[str, Any]]:
"""Creates all the configurations based on the command line arguments."""
print(kwargs)
args = []
for k, v in kwargs.items():
if isinstance(v, str):
args.append([(k, s) for s in v.split(",")])
else:
args.append([(k, v)])
configs = list(itertools.product(*args))
return [dict(c) for c in configs]


def make_dataframe_from_benchmark_data(data: dict) -> Any:
"""Creates a dataframe from the received data."""
import pandas

Check warning on line 723 in onnxscript/tools/benchmark/benchmark_helpers.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_helpers.py#L723

Added line #L723 was not covered by tests

return pandas.DataFrame(data)

Check warning on line 725 in onnxscript/tools/benchmark/benchmark_helpers.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_helpers.py#L725

Added line #L725 was not covered by tests
53 changes: 53 additions & 0 deletions onnxscript/tools/benchmark/benchmark_helpers_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import unittest

import onnxscript.tools.benchmark.benchmark_helpers as bh


class BenchmarkHelperTest(unittest.TestCase):
def test_make_configs(self):
value = {
"warmup": 5,
"model": "llama,phi",
"device": "cpu,cuda",
"config": "medium",
"dump_folder": "",
}
self.assertTrue(bh.multi_run(value))
configs = bh.make_configs(value)
expected = [
{
"warmup": 5,
"model": "llama",
"device": "cpu",
"config": "medium",
"dump_folder": "",
},
{
"warmup": 5,
"model": "llama",
"device": "cuda",
"config": "medium",
"dump_folder": "",
},
{
"warmup": 5,
"model": "phi",
"device": "cpu",
"config": "medium",
"dump_folder": "",
},
{
"warmup": 5,
"model": "phi",
"device": "cuda",
"config": "medium",
"dump_folder": "",
},
]
self.assertEqual(expected, configs)


if __name__ == "__main__":
unittest.main(verbosity=2)

Check warning on line 53 in onnxscript/tools/benchmark/benchmark_helpers_test.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_helpers_test.py#L53

Added line #L53 was not covered by tests
129 changes: 129 additions & 0 deletions onnxscript/tools/benchmark/benchmark_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
from __future__ import annotations

import multiprocessing
import os
import platform
import re
import subprocess
import sys


class BenchmarkError(RuntimeError):
pass


def get_machine() -> dict[str, str | int | float | tuple[int, int]]:
"""Returns the machine specification."""
cpu: dict[str, str | int | float | tuple[int, int]] = dict(

Check warning on line 19 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L19

Added line #L19 was not covered by tests
xadupre marked this conversation as resolved.
Show resolved Hide resolved
machine=str(platform.machine()),
processor=str(platform.processor()),
version=str(sys.version),
cpu=int(multiprocessing.cpu_count()),
executable=str(sys.executable),
)
try:
import torch.cuda
Fixed Show fixed Hide fixed
except ImportError:
return cpu

Check warning on line 29 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L26-L29

Added lines #L26 - L29 were not covered by tests

cpu["has_cuda"] = bool(torch.cuda.is_available())

Check warning on line 31 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L31

Added line #L31 was not covered by tests
if cpu["has_cuda"]:
cpu["capability"] = torch.cuda.get_device_capability(0)
cpu["device_name"] = str(torch.cuda.get_device_name(0))
return cpu

Check warning on line 35 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L33-L35

Added lines #L33 - L35 were not covered by tests


def _cmd_line(script_name: str, **kwargs: dict[str, str | int | float]) -> list[str]:
args = [sys.executable, "-m", script_name]

Check warning on line 39 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L39

Added line #L39 was not covered by tests
for k, v in kwargs.items():
args.append(f"--{k}")
args.append(str(v))
return args

Check warning on line 43 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L41-L43

Added lines #L41 - L43 were not covered by tests


def _extract_metrics(text: str) -> dict[str, str]:
reg = re.compile(":(.*?),(.*.?);")
res = reg.findall(text)

Check warning on line 48 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L47-L48

Added lines #L47 - L48 were not covered by tests
if len(res) == 0:
return {}
return dict(res)

Check warning on line 51 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L50-L51

Added lines #L50 - L51 were not covered by tests


def _make_prefix(script_name: str, index: int) -> str:
name = os.path.splitext(script_name)[0]
return f"{name}_dort_c{index}_"

Check warning on line 56 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L55-L56

Added lines #L55 - L56 were not covered by tests


def run_benchmark(
script_name: str,
configs: list[dict[str, str | int | float]],
verbose: int = 0,
stop_if_exception: bool = True,
dort_dump: bool = False,
) -> list[dict[str, str | int | float | tuple[int, int]]]:
"""
Runs a script multiple times and extract information from the output
following the pattern ``:<metric>,<value>;``.

:param script_name: python script to run
:param configs: list of execution to do
:param stop_if_exception: stop if one experiment failed, otherwise continue
:param verbose: use tqdm to follow the progress
:param dort_dump: dump onnx file if dort is used
:return: values
"""
if verbose:
from tqdm import tqdm

Check warning on line 78 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L78

Added line #L78 was not covered by tests
Fixed Show fixed Hide fixed

loop = tqdm(configs)

Check warning on line 80 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L80

Added line #L80 was not covered by tests
else:
loop = configs

Check warning on line 82 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L82

Added line #L82 was not covered by tests

data: list[dict[str, str | int | float | tuple[int, int]]] = []

Check warning on line 84 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L84

Added line #L84 was not covered by tests
for i, config in enumerate(loop):
cmd = _cmd_line(script_name, **config)

Check warning on line 86 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L86

Added line #L86 was not covered by tests

if dort_dump:
os.environ["ONNXRT_DUMP_PATH"] = _make_prefix(script_name, i)

Check warning on line 89 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L89

Added line #L89 was not covered by tests
else:
os.environ["ONNXRT_DUMP_PATH"] = ""

Check warning on line 91 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L91

Added line #L91 was not covered by tests
if verbose > 3:
print(f"[run_benchmark] cmd={cmd if isinstance(cmd, str) else ' '.join(cmd)}")
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Fixed Show fixed Hide fixed
res = p.communicate()
out, err = res
sout = out.decode("utf-8", errors="ignore")
serr = err.decode("utf-8", errors="ignore")

Check warning on line 98 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L93-L98

Added lines #L93 - L98 were not covered by tests

if "ONNXRuntimeError" in serr or "ONNXRuntimeError" in sout:
if stop_if_exception:
raise RuntimeError(

Check warning on line 102 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L102

Added line #L102 was not covered by tests
f"Unable to continue with config {config} due to the "
f"following error\n{serr}"
f"\n----OUTPUT--\n{sout}"
)

metrics = _extract_metrics(sout)

Check warning on line 108 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L108

Added line #L108 was not covered by tests
if len(metrics) == 0:
if stop_if_exception:
Fixed Show fixed Hide fixed
raise BenchmarkError(

Check warning on line 111 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L111

Added line #L111 was not covered by tests
f"Unable (2) to continue with config {config}, no metric was "
f"collected.\n--ERROR--\n{serr}\n--OUTPUT--\n{sout}"
)
else:
metrics = {}
metrics.update(config)
metrics["ERROR"] = serr
metrics["OUTPUT"] = sout
metrics["CMD"] = f"[{' '.join(cmd)}]"
data.append(metrics)

Check warning on line 121 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L116-L121

Added lines #L116 - L121 were not covered by tests
Fixed Show fixed Hide fixed
if verbose > 5:
print("--------------- ERROR")
print(serr)

Check warning on line 124 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L123-L124

Added lines #L123 - L124 were not covered by tests
if verbose >= 10:
print("--------------- OUTPUT")
print(sout)

Check warning on line 127 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L126-L127

Added lines #L126 - L127 were not covered by tests

return data

Check warning on line 129 in onnxscript/tools/benchmark/benchmark_run.py

View check run for this annotation

Codecov / codecov/patch

onnxscript/tools/benchmark/benchmark_run.py#L129

Added line #L129 was not covered by tests
Loading
Loading