8
PythonDispatcher class is a thin python-binding to C++ dispatcher and it
9
is designed to show how dispatcher precompute works. In particular,
10
it shows for a certain op `foo`, what the computed dispatch table looks
11
like after user register their kernels to certains dispatch keys.
13
In the real C++ dispatcher we support many dispatch keys for different
14
functionalities. For simplicity PythonDispatcher only supports dispatch
15
keys for a single example of each use case. These use cases are listed below:
17
- CPU/AutogradCPU: represents in-tree backends which we usually have dedicated inference &
18
autograd kernel in pytorch core library.
20
- FPGA/AutogradOther: represents in-tree backends which we usually have backend specific
21
inference kernels, but they share the same autograd kernel specified in AutogradOther.
22
E.g. FPGA, SparseCsrCPU
23
- XLA/AutogradXLA: represents out-of-tree backends which we don't have either inference or autograd
24
kernel defined in pytorch core library. Backend owner is responsible for registering both
25
inference & autograd kernels in their extensions(e.g. torch-xla) for the operators they support.
27
- CompositeExplicitAutograd: alias key mapped to inference kernels of all backends like CPU, CUDA, XLA etc.
28
Kernels registered to this key MUST work for inference for all backends.
29
- Autograd: alias key mapped to autograd of all backends like AutogradCPU, AutogradXLA, AutogradOther.
30
Kernels registered to this key MUST work for autograd for all backends.
31
- CompositeImplicitAutograd: alias key CompositeImplicitAutograd = CompositeExplicitAutograd + Autograd
32
Kernels registered to this key MUST work for both inference + autograd for all backends.
34
Note we only allow registrations to alias keys inside pytorch core library. E.g
35
you shouldn't register a CompositeImplicitAutograd or CompositeExplicitAutograd
36
kernel from torch-xla extension, instead you should upstream the kernel into
37
pytorch/pytorch repo so that it's available for all backends and continuously
38
tested even without the extension.
41
dispatcher = PythonDispatcher()
42
dispatcher.register(["CPU", "XLA", "CompositeImplicitAutograd"])
43
print(dispatcher.dispatchTable()) # This tells you exactly which kernel is used for certain backend.
44
# For more debugging information
45
# print(dispatcher.keys())
46
# print(dispatcher.registrations())
47
# print(dispatcher.rawRegistrations())
48
# print(dispatcher.rawDispatchTable())
49
PythonDispatcher calls C++ dispatcher under the hood for to precompute dispatch table.
50
This file only provides the simplified API for developers, relevant test code is located in
55
class PythonDispatcher:
56
namespace = "__test__"
61
"FPGA", "AutogradOther",
63
"Lazy", "AutogradLazy",
67
"CompositeExplicitAutograd",
69
"CompositeImplicitAutograd",
71
supported_keys = runtime_keys + alias_keys
73
def __init__(self) -> None:
74
C._dispatch_check_invariants(self.name)
75
self.ref = C._dispatch_library("FRAGMENT", self.namespace, "")
76
self.ref.def_("foo(Tensor x) -> Tensor")
79
Returns a list of dispatch keys supported by PythonDispatcher.
80
You can register kernels to these keys.
84
return self.supported_keys
87
Register kernels to the target dispatchKeys.
88
dispatchKeys(list[str]): a list of dispatch keys that you want to register
89
your own kernel. Note that you don't need to write the kernel yourself in
90
this PythonDispatcher.E.g. for CPU key, a kernel(e.g fn_CPU for CPU) is
91
automatically generated and registered.
94
def register(self, dispatchKeys):
96
if len(set(dispatchKeys)) != len(dispatchKeys):
98
f"Overriden is not allowed but found duplicates in {dispatchKeys}."
102
"CompositeImplicitAutograd" in dispatchKeys
103
and "CompositeExplicitAutograd" in dispatchKeys
106
"Registration to both CompositeImplicitAutograd and CompositeExplicitAutograd is not allowed."
108
for key in dispatchKeys:
109
if key not in self.supported_keys:
111
f"{key} is not supported, please select a dispatch key in {self.supported_keys}."
113
self.ref.impl_t_t("foo", dispatch=key, debug="fn_" + key)
116
Helper function to format (key, kernel).
119
def _format_line(self, key, kernel):
120
return f"{key:<15} {kernel}\n"
123
Helper function to print a table header.
126
def _format_header(self, header):
130
s += self._format_line("key", "kernel")
131
s += "---------------------------\n"
135
Returns raw output of all registration info for debugging only.
136
Use registrations() for a simplified version.
139
def rawRegistrations(self):
140
return C._dispatch_dump(f"{self.namespace}::{self.name}")
143
Returns raw output of computed dispatch table for debugging only.
144
Use dispatchTable() for a simplified version.
147
def rawDispatchTable(self):
148
return C._dispatch_dump_table(f"{self.namespace}::{self.name}")
151
Returns a table(str) including all the registrations from users.
152
Note this includes registrations to both runtime keys and alias keys.
155
def registrations(self):
156
output = self._format_header("Registered Kernels")
157
state = self.rawRegistrations()
158
state_entries = state.split("\n")
159
for line in state_entries:
160
first = line.split(":")[0]
161
if any(first.startswith(k) for k in self.supported_keys):
162
kernel = line.split("::")[0].split(" ")[1]
163
output += self._format_line(first, kernel)
167
Returns the computed dispatch table(str). Note this only include
168
runtime keys, registrations to alias keys have been decoded to their
172
def dispatchTable(self):
173
output = self._format_header("Computed Dispatch Table")
174
table = self.rawDispatchTable()
175
table_entries = table.split("\n")
176
regex = re.compile(r"registered at .*FallbackKernel\.cpp.*(\[)")
177
for line in table_entries:
178
k = line.split(":")[0]
179
if k in self.runtime_keys:
180
entry = regex.sub("[", line)
181
output += self._format_line(k, entry.split(": ")[1])