diff --git a/notes.md b/notes.md index ab9a9998aee99c5916a090e2b7f8eadcea12e9cd..1f1fa5dcde7fc0dcbcaca66970443201d58ff480 100644 --- a/notes.md +++ b/notes.md @@ -7,9 +7,7 @@ - Useful when we want to support multiple knobs in an op - Application: - - List of operators (just a name) -- constant - - List of knobs (each with a name) -- constant - - List of knobs (by knob name) for each layer -- constant + - List of knobs for each operator -- constant - (We provide a knob value for "baseline" and sneak that in each layer) - Argparser extra arguments -- static method - How to measure QoS & performance given a configuration -- method diff --git a/predtuner/__init__.py b/predtuner/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/predtuner/apps/__init__.py b/predtuner/apps/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/predtuner/apps/approxapp.py b/predtuner/apps/approxapp.py new file mode 100644 index 0000000000000000000000000000000000000000..b36a62adae1fd11e7eea5e8b5d5850bf1be9ab70 --- /dev/null +++ b/predtuner/apps/approxapp.py @@ -0,0 +1,71 @@ +import abc +from pathlib import Path +from typing import Dict, List, Tuple, Union + + +class ApproxKnob: + def __init__(self, name: str, **kwargs): + self.name = name + self.kwargs = kwargs + + def coexists_with(self, other: "ApproxKnob") -> bool: + return False + + def __repr__(self): + return f'Knob"{self.name}"({self.kwargs})' + + +KnobsT = Dict[str, str] +PathLike = Union[Path, str] + + +class ApproxApp(abc.ABC): + """Generic approximable application with operator & knob enumeration, + and measures its own QoS and performance given a configuration.""" + + @property + @abc.abstractmethod + def op_knobs(self) -> Dict[str, List[ApproxKnob]]: + """Get a mapping from each operator (identified by str) to a list of applicable knobs.""" + pass + + @abc.abstractmethod + def measure_qos_perf( + self, with_approxes: KnobsT, is_testset: bool + ) -> Tuple[float, float]: + pass + + def get_tuner(self) -> "ApproxTuner": + """We implement this function. Sets up an ApproxTuner instance + which the user can directly call `tune()` on with opentuner parameters.""" + return ApproxTuner() # TODO + + +class Config: + pass # TODO: work this out later + + +class ApproxTuner: + def tune( + self, + qos_threshold: float, + accuracy_convention: str = "absolute", + **kwargs, # many opentuner parameters with defaults, omitted + ): + """Generate an optimal set of approximation configurations for the model.""" + pass # TODO + + # More helpers for selecting a config omitted for brevity + + def get_all_configs(self) -> List[Config]: + return [] # TODO + + # TODO + # Work out details of saving / loading + # Important to keep association between model, weights, and configs + # Especially when retraining is involved + def store_configs(self, path: PathLike): + pass + + def load_configs(self, path: PathLike): + pass diff --git a/predtuner/apps/modeledapp.py b/predtuner/apps/modeledapp.py new file mode 100644 index 0000000000000000000000000000000000000000..699f7a913e026fab2bbdb4d40e07dbb606de2064 --- /dev/null +++ b/predtuner/apps/modeledapp.py @@ -0,0 +1,103 @@ +import abc +from typing import Dict, Tuple + +import torch + +from .approxapp import ApproxApp, ApproxKnob, KnobsT + + +class ModeledApp(ApproxApp, abc.ABC): + """Approximable application that inherits at least 1 interface for performance/QoS modeling. + + It's invalid to inherit from this class without also implementing at least 1 interface + provided in this set of API; + for non-modeling application, inherit from `ApproxApp` instead. + """ + + @abc.abstractmethod + def measure_qos(self, with_approxes: KnobsT, is_testset: bool) -> float: + """User should fill in this hole if not using any QoS model. + Otherwise this function will not be called and can be empty.""" + pass + + @abc.abstractmethod + def measure_perf(self, with_approxes: KnobsT, is_testset: bool) -> float: + """User should fill in this hole if not using any performance model. + Otherwise this function will not be called and can be empty.""" + pass + + def measure_qos_perf( + self, + with_approxes: KnobsT, + is_testset: bool, + perf_model: str = "none", + qos_model: str = "none", + ) -> Tuple[float, float]: + """We provide this with the right qos and perf function. + + Need to detect self capability using `isinstance(self, ...)` + and check input parameter, to decide which model to use. + The non-modeled part will be obtained by calling the respective + `measure_qos` and `measure_perf` function (a bit of dirty + dispatching work to do here). + """ + pass + + +class IPerfModeled(abc.ABC): + """Interface to be inherited by user App which allows performance to be model-derived.""" + + @property + @abc.abstractmethod + def op_knobs_cost(self) -> Dict[str, Dict[ApproxKnob, float]]: + """Get a scalar cost of each operator applied with each knob. + The ops and knobs listed here should be strictly equal to `ApproxApp.ops_knobs()`""" + pass + + def measure_perf(self, with_approxes: KnobsT, is_testset: bool) -> float: + """We implement this using a weighted linear performance model.""" + pass + + +class IQoSModeledP1(abc.ABC): + """Interface that allows QoS model `P1` to be applied to user-defined App.""" + + @abc.abstractmethod + def get_tensor_output( + self, with_approxes: KnobsT, is_testset: bool + ) -> torch.Tensor: + """Run the tensor-based application with config `with_approxes` applied, + and return a single tensor result. + + Note that while we require the return value to be a PyTorch tensor, + user is free to implement this on non-PyTorch applications. + """ + pass + + @abc.abstractmethod + def qos_from_output(self, tensor_output: torch.Tensor) -> float: + """Compute a Quality of Service level from the tensor output of application.""" + pass + + def measure_qos(self, with_approxes: KnobsT, is_testset: bool) -> float: + """We implement this using a QoS model P1.""" + pass + + +class IQoSModeledP2(abc.ABC): + """Interface that allows QoS model `P2` to be applied to user-defined App.""" + + @abc.abstractmethod + def _measure_qos(self, with_approxes: KnobsT, is_testset: bool) -> torch.Tensor: + """An internal QoS-measuring method that does the same thing as `measure_qos_p2`. + + The point is P2 queries some QoS results and caches them before tuning starts, + and then defines a `measure_qos` that doesn't run the application during tuning + (to reduce overhead). + """ + pass + + def measure_qos(self, with_approxes: KnobsT, is_testset: bool) -> float: + """We implement this using a QoS model P1.""" + pass + diff --git a/predtuner/apps/torchapp.py b/predtuner/apps/torchapp.py new file mode 100644 index 0000000000000000000000000000000000000000..da2f3c6445b49aabf8cca13d00fa5d05694f28be --- /dev/null +++ b/predtuner/apps/torchapp.py @@ -0,0 +1,39 @@ +import abc +from typing import Set + +from torch.utils.data.dataloader import DataLoader + +from .approxapp import ApproxKnob +from .modeledapp import IPerfModeled, IQoSModeledP1, IQoSModeledP2, ModeledApp + + +class TorchApproxKnob(ApproxKnob): + """Defines an approximation knob that knows + its own expected speedup ratio and what Modules it can apply to, + and can be applied to a torch.nn.Module to return an approximated Module.""" + + pass + + +class TorchApp(ModeledApp, IPerfModeled, IQoSModeledP1, IQoSModeledP2, abc.ABC): + """Approximable PyTorch Modules (tensor output assumed). + + Automatically derives performance model and QoS models P1&P2.""" + + @property + @abc.abstractmethod + def all_knobs(self) -> Set[TorchApproxKnob]: + """User defines a set of all knobs available; we'll dispatch them to each layer (op).""" + pass + + @abc.abstractmethod + def get_input_data(self, testset: bool) -> DataLoader: + """User defines the input dataset to traverse.""" + pass + + # User also needs to define `IQoSModeledP1.qos_from_output` (QoS metric, omitted) + + # We implement `ApproxApp.op_knobs`, + # `IPerfModeled.op_knobs_cost`, + # `IQoSModeledP1.get_tensor_output` + # and `IQoSModeledP2._measure_qos`. (Omitted)