At the top of high-level libtorch C++ API is the at::Tensor
class. It provides a comprehensive set
of methods for tensor storage management, data initialization, auto-differentiation, as well as all
basic tensor operations. It is defined at
ATen/core/Tensor.h
Tensor
class itself does not implement much functionality. Storage management is done in
TensorImpl
class, and Tensor
just holds a reference-counting pointer to it. That allows to have
several tensors referencing to the same storage (or slices of it).
Tensor
dispatches tensor methods to proper implementation, depending on data type and backend.
The routing happens in the TensorImpl::type()
method, and at the top level all tensor
operations look like e.g.
inline Tensor Tensor::log10() const {
return type().log10(*this);
}
(implemented in ATen/core/TensorMethods.h)
Source ATen/core/TensorImpl.h.
TensorImpl
holds the tensor data (as at::Storage storage_
member) along with the tensor's
dimensions and strides.
TensorImpl
also implements the routing for tensor operations in TensorImpl::type()
method. The
actual routing is defined in the LegacyTypeDispatch
singleton. It holds a rectangular matrix of
Type
elements. One dimension of that matrix corresponds to scalar types (e.g. int
or float
),
and another one to backends (sparse or dense, CUDA or CPU).
(source at ATen/core/LegacyTypeDispatch.h)
Type
is the base class for backend- and type-specific operations on tensors. Its source is a good
reference on what operations a tensor should support:
ATen/core/Type.h.
Code for classes that inherit from Type
are generated at the build time. There is a
Type
-derived class for each combination of the backend and scalar type. Entire Type
hierarchy
looks like this:
Code generation is a stage in CMake. That is, one has to run the build to see the generated files. A
Python script, gen.py
, takes templates of C++ .h
and .cpp
files from ATen/templates/
directory, and expands ${...}
placeholders with type- and backend-specific code (and comments!).
There are three mechanisms to generate code in PyTorch:
- cwrap, or legacy, that creates Python bindings to TH and THC functions;
- native (currently preferred), that binds Python code to C++ implementations;
- nn, for generating NN-specific bindings, e.g., for loss and activation functions, convolutions, pooling, etc.
gen.py
implements all three mechanisms, and dispatches to the right one depending on the input
file name or extension. Metadata for the bindings is stored in the following files:
- ATen/Declarations.cwrap for cwrap bindings;
- ATen/native/native_functions.yaml for native methods;
- ATen/nn.yaml for nn operations;
- /tools/autograd/derivatives.yaml derivatives for native and nn methods.
ATen/native/README.md has a very detailed explanation of the code generation process from PyTorch contributor's point of view (mostly covering the native method).
C++ code for Python integration is in torch/csrc/
directory. It has a
README.md (mostly with
information for PyTorch contributors).