Skip to content

Commit bfc8a3e

Browse files
ezyangsoumith
authored andcommitted
Reference counting documentation. (pytorch#1520)
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
1 parent 6fab621 commit bfc8a3e

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

torch/lib/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,97 @@ multiple variants of the library, summarized here:
1212
* THS = TorcH Sparse
1313

1414
(You'll also see these abbreviations show up in symbol names.)
15+
16+
## Reference counting
17+
18+
PyTorch employs reference counting in order to permit tensors to provide
19+
differing views on a common underlying storage. For example, when you call
20+
view() on a Tensor, a new THTensor is allocated with differing dimensions,
21+
but it shares the same THStorage with the original tensor.
22+
23+
Unfortunately, this means we are in the business of manually tracking reference
24+
counts inside our C library code. Fortunately, for most of our library code implementing
25+
tensor operations, there is only one rule you have to remember:
26+
27+
> **Golden Rule of Reference Counting:** You must either FREE or RETURN
28+
> a pointer which was returned by a function whose name begins with
29+
> `new` or which you called `retain` on.
30+
> If you return this pointer, your function name must begin with `new`.
31+
32+
In a long function, there may be many invocations of functions with `new` in
33+
their name. Your responsibility is to go through each of them and ensure
34+
that there is a matching `free` for it for EACH exit point of the function.
35+
36+
### Examples
37+
38+
Suppose you want to get a reference to the indices of a sparse tensor. This
39+
function is called `newIndices`. The `new` means you MUST free it when you're
40+
done (usually at the end of your function.) (It's worth noting that
41+
`newIndices` doesn't actually allocate a fresh indices tensor; it just gives
42+
you a pointer to the existing one.) DO NOT directly access the member
43+
variables of the struct.
44+
45+
```
46+
THIndexTensor *indices = THSTensor_(newIndices)(state, sparse);
47+
// ... do some stuff ...
48+
THIndexTensor_(free)(state, indices);
49+
```
50+
51+
Let's take a look at the implementation of `newIndices`. This doesn't free the
52+
return result of `newNarrow`, but returns it. This justifies the `new` in its
53+
name.
54+
55+
```
56+
THIndexTensor *THSTensor_(newIndices)(const THSTensor *self) {
57+
// ...
58+
return THIndexTensor_(newNarrow)(self->indices, 1, 0, self->nnz);
59+
}
60+
```
61+
62+
Passing an object to another function does NOT absolve you of responsibility
63+
of freeing it. If that function holds on to a pointer to the object, it
64+
will `retain` it itself.
65+
66+
```
67+
THLongStorage *inferred_size = THLongStorage_newInferSize(size, numel);
68+
THTensor_(setStorage)(self, tensor->storage, tensor->storageOffset, inferred_size, NULL);
69+
THLongStorage_free(inferred_size);
70+
```
71+
72+
Sometimes, you have a tensor in hand which you'd like to use directly, but
73+
under some conditions you have to have to call, e.g., `newContiguous`, to get
74+
it into the correct form:
75+
76+
```
77+
if (!(k_->stride[3] == 1) || !(k_->stride[2] == k_->size[3])) {
78+
kernel = THTensor_(newContiguous)(k_);
79+
} else {
80+
THTensor_(retain)(k_);
81+
kernel = k_;
82+
}
83+
...
84+
THTensor_(free)(kernel);
85+
```
86+
87+
In this case, we have (redundantly) called `retain` on `k_`, so that we can
88+
unconditionally free `kernel` at the end of the function; intuitively, you
89+
want it to be possible to replace the conditional expression with an equivalent
90+
function call, e.g., `kernel = THTensor_(newContiguous2D)(k_)`.
91+
92+
### Tips
93+
94+
* If you have an early exit in a function (via a `return`), don't forget to
95+
`free` any pointers which you allocated up to this point. If at all possible,
96+
move early exits prior to these allocations, so that you don't have to clean up.
97+
98+
* Very occasionally, you may be able to implement an algorithm more efficiently
99+
if you "destroy" its input. This is a `move`; after moving an object away,
100+
you must NOT `free` it. This is the one exception to the rule, and at the
101+
moment there is only one instance of `move` in the code base.
102+
103+
* We use `THError` to signal error cases, and fortunately,
104+
you do NOT need to make sure you've freed everything before calling `THError`,
105+
because by default, it aborts the entire process. However, it's good style
106+
to call `THError` before performing any allocations, since in some cases we
107+
sketchily throw a C++ exception and try to recover (in particular, the test
108+
suite does this.)

0 commit comments

Comments
 (0)