(
self,
name,
choices: List[ChoiceCaller],
input_nodes,
layout,
# optional dict mapping arg indices to the functions
# generating a torch.Tensor for that input from the
# corresponding ir.Buffer. if passed for a given
# arg, the function will be called instead of
# generating a random torch.Tensor for benchmarking.
input_gen_fns: Optional[Dict[int, Callable[[ir.Buffer], torch.Tensor]]] = None,
)
| 703 | |
| 704 | class AlgorithmSelectorCache(PersistentCache): |
| 705 | def __call__( |
| 706 | self, |
| 707 | name, |
| 708 | choices: List[ChoiceCaller], |
| 709 | input_nodes, |
| 710 | layout, |
| 711 | # optional dict mapping arg indices to the functions |
| 712 | # generating a torch.Tensor for that input from the |
| 713 | # corresponding ir.Buffer. if passed for a given |
| 714 | # arg, the function will be called instead of |
| 715 | # generating a random torch.Tensor for benchmarking. |
| 716 | input_gen_fns: Optional[Dict[int, Callable[[ir.Buffer], torch.Tensor]]] = None, |
| 717 | ): |
| 718 | from .codegen.cuda.cuda_kernel import CUDATemplateCaller |
| 719 | |
| 720 | # TODO(nmacchioni): remove once CI tests are fixed |
| 721 | choices = [choice for choice in choices if choice is not None] |
| 722 | if len(choices) == 0: |
| 723 | raise RuntimeError( |
| 724 | "No choices to select, please consider adding ATEN into max_autotune_gemm_backends " |
| 725 | "config (defined in torch/_inductor/config.py) to allow at least one choice. " |
| 726 | ) |
| 727 | log.info("Max autotune selects from %s choices.", str(len(choices))) |
| 728 | |
| 729 | if len(choices) == 1: |
| 730 | if not isinstance(choices[0], CUDATemplateCaller): |
| 731 | # CUDATemplateCaller still needs to go through autotuning process to retrieve workspace size. |
| 732 | return choices[0].output_node() |
| 733 | |
| 734 | @functools.lru_cache(None) |
| 735 | def make_benchmark_fn(): |
| 736 | return self.make_benchmark_fn(choices, input_nodes, layout, input_gen_fns) |
| 737 | |
| 738 | def autotune(choices): |
| 739 | return make_benchmark_fn()(choices) |
| 740 | |
| 741 | if config.autotune_in_subproc: |
| 742 | from .autotune_process import tuning_pool |
| 743 | |
| 744 | # do the optional warmup |
| 745 | tuning_pool.initialize() |
| 746 | |
| 747 | autotune_start_ts = time.time() |
| 748 | timings = self.lookup( |
| 749 | choices, |
| 750 | name, |
| 751 | repr([self.key_of(x) for x in input_nodes]), |
| 752 | autotune, |
| 753 | ) |
| 754 | autotune_elapse = time.time() - autotune_start_ts |
| 755 | if timings == {} or choices[0] not in timings: |
| 756 | return choices[0].output_node() |
| 757 | |
| 758 | if make_benchmark_fn.cache_info().currsize: |
| 759 | counters["inductor"]["select_algorithm_autotune"] += 1 |
| 760 | if ( |
| 761 | make_benchmark_fn.cache_info().currsize |
| 762 | or log.getEffectiveLevel() == logging.DEBUG |
nothing calls this directly
no test coverage detected