You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recommend cpython agnosticism in cpp custom op tutorial (#3250)
* Recommend python agnosticism in cpp custom op tutorial
* forgot to delete a line
* Fixed tutorial to be clearer and to recommend other path
* Switch to commits instead of master
* Formatting code blocks
* polish + i missed some code blocks earlier
* Adjust advice based on Sam Gross's knowlede
@@ -252,16 +292,89 @@ matters (importing in the wrong order will lead to an error).
252
292
253
293
To use the custom operator with hybrid Python/C++ registrations, we must
254
294
first load the C++ library that holds the custom operator definition
255
-
and then call the ``torch.library`` registration APIs. This can happen in one
256
-
of two ways:
257
-
258
-
1. If you're following this tutorial, importing the Python C extension module
259
-
we created will load the C++ custom operator definitions.
260
-
2. If your C++ custom operator is located in a shared library object, you can
261
-
also use ``torch.ops.load_library("/path/to/library.so")`` to load it. This
262
-
is the blessed path for Python agnosticism, as you will not have a Python C
263
-
extension module to import. See `torchao __init__.py <https://github.com/pytorch/ao/blob/881e84b4398eddcea6fee4d911fc329a38b5cd69/torchao/__init__.py#L26-L28>`_
264
-
for an example.
295
+
and then call the ``torch.library`` registration APIs. This can happen in
296
+
three ways:
297
+
298
+
299
+
1. The first way to load the C++ library that holds the custom operator definition
300
+
is to define a dummy Python module for _C. Then, in Python, when you import the
301
+
module with ``import _C``, the ``.so`` files corresponding to the extension will
302
+
be loaded and the ``TORCH_LIBRARY`` and ``TORCH_LIBRARY_IMPL`` static initializers
303
+
will run. One can create a dummy Python module with ``PYBIND11_MODULE`` like below,
304
+
but you will notice that this does not compile with ``Py_LIMITED_API``, because
305
+
``pybind11`` does not promise to only use the stable limited CPython API! With
306
+
the below code, you sadly cannot build a CPython agnostic wheel for your extension!
307
+
(Foreshadowing: I wonder what the second way is ;) ).
308
+
309
+
.. code-block:: cpp
310
+
311
+
// in, say, not_agnostic/csrc/extension_BAD.cpp
312
+
#include <pybind11/pybind11.h>
313
+
314
+
PYBIND11_MODULE("_C", m) {}
315
+
316
+
.. code-block:: python
317
+
318
+
# in, say, extension/__init__.py
319
+
from . import _C
320
+
321
+
2. In this tutorial, because we value being able to build a single wheel across multiple
322
+
CPython versions, we will replace the unstable ``PYBIND11`` call with stable API calls.
323
+
The below code compiles with ``-DPy_LIMITED_API=0x03090000`` and successfully creates
324
+
a dummy Python module for our ``_C`` extension so that it can be imported from Python.
325
+
See `extension_cpp/__init__.py <https://github.com/pytorch/extension-cpp/blob/38ec45e/extension_cpp/__init__.py>`_
326
+
and `extension_cpp/csrc/muladd.cpp <https://github.com/pytorch/extension-cpp/blob/38ec45e/extension_cpp/csrc/muladd.cpp>`_
327
+
for more details:
328
+
329
+
.. code-block:: cpp
330
+
331
+
#include <Python.h>
332
+
333
+
extern "C" {
334
+
/* Creates a dummy empty _C module that can be imported from Python.
335
+
The import from Python will load the .so consisting of this file
336
+
in this extension, so that the TORCH_LIBRARY static initializers
337
+
below are run. */
338
+
PyObject* PyInit__C(void)
339
+
{
340
+
static struct PyModuleDef module_def = {
341
+
PyModuleDef_HEAD_INIT,
342
+
"_C", /* name of module */
343
+
NULL, /* module documentation, may be NULL */
344
+
-1, /* size of per-interpreter state of the module,
345
+
or -1 if the module keeps state in global variables. */
346
+
NULL, /* methods */
347
+
};
348
+
return PyModule_Create(&module_def);
349
+
}
350
+
}
351
+
352
+
.. code-block:: python
353
+
354
+
# in, say, extension/__init__.py
355
+
from . import _C
356
+
357
+
3. If you want to avoid ``Python.h`` entirely in your C++ custom operator, you may
358
+
use ``torch.ops.load_library("/path/to/library.so")`` in Python to load the ``.so``
359
+
file(s) compiled from the extension. Note that, with this method, there is no ``_C``
360
+
Python module created for the extension so you cannot call ``import _C`` from Python.
361
+
Instead of relying on the import statement to trigger the custom operators to be
362
+
registered, ``torch.ops.load_library("/path/to/library.so")`` will do the trick.
363
+
The challenge then is shifted towards understanding where the ``.so`` files are
364
+
located so that you can load them, which is not always trivial:
0 commit comments