Skip to content

Commit

Permalink
add code to run multiple export in one call
Browse files Browse the repository at this point in the history
Signed-off-by: Xavier Dupre <[email protected]>
  • Loading branch information
xadupre committed Jun 20, 2024
1 parent ae4c67d commit d0a3ee5
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 112 deletions.
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 @@ def run_onnx_inference(
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
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

Check notice

Code scanning / lintrunner

PYLINT/C0415 Note test

Import outside toplevel (torch.cuda) (import-outside-toplevel)
See import-outside-toplevel. To disable, use # pylint: disable=import-outside-toplevel
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 notice

Code scanning / lintrunner

PYLINT/C0415 Note test

Import outside toplevel (tqdm.tqdm) (import-outside-toplevel)
See import-outside-toplevel. To disable, use # pylint: disable=import-outside-toplevel

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

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)

Check notice

Code scanning / lintrunner

PYLINT/R1732 Note test

Consider using 'with' for resource-allocating operations (consider-using-with)
See consider-using-with. To disable, use # pylint: disable=consider-using-with
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:

Check notice

Code scanning / lintrunner

PYLINT/R1720 Note test

Unnecessary "else" after "raise", remove the "else" and de-indent the code inside it (no-else-raise)
See no-else-raise. To disable, use # pylint: disable=no-else-raise
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 failure

Code scanning / lintrunner

MYPY/arg-type Error test

Argument 1 to "append" of "list" has incompatible type "dict[str, str]"; expected "dict[str, str | int | float | tuple[int, int]]" To disable, use # type: ignore[arg-type]

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
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

0 comments on commit d0a3ee5

Please sign in to comment.