@@ -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