mirror of
https://github.com/zebrajr/pytorch.git
synced 2025-12-06 12:20:52 +01:00
Summary: Pull Request resolved: https://github.com/pytorch/pytorch/pull/30894 This PR begins the process of removing BUILD_NAMEDTENSOR macros. There will be followups. Reasons for removing the macros: - BUILD_NAMEDTENSOR is always on and has been on since pytorch 1.3.0. - Since we don't test building without it, it is useless to keep around. - Code becomes nicer to read without the macros Reasons for not removing the macros: - potential for feature flagging Now, I argue against needing to feature flag. The main reason why we might want to feature flag is if we need to disable the feature. We'd need a fast switch to disable the feature if someone discovers in the future that named tensors caused some regression in some existing workflows. In https://github.com/pytorch/pytorch/pull/25798, I did a variety of macro- and micro- benchmarks to determine the performance impact of named tensors on regular tensors. [The microbenchmarks](https://github.com/pytorch/pytorch/pull/25798#issuecomment-529014810) were not very stable, and running the microbenchmarks for more iterations doesn't actually help because the noise is not distributed in a nice way. Instead of microbenchmarks I ran a [profiler (perf)](https://github.com/pytorch/pytorch/pull/25798#issuecomment-555707645) to estimate how much overhead named tensors add to unnamed code. I estimated the overhead to be less than 100ns for `add` and even smaller for `mm`; there are ways to optimize even futher if we find this to be a problem. [Initial macrobenchmarks](https://github.com/pytorch/pytorch/pull/25798#issuecomment-530539104) were also not very stable. I ran imagenet for some number of epochs. To make them more stable, I got rid of the data loading (which seemed to vary between runs). [In some benchmarkers without data loading](https://github.com/pytorch/pytorch/pull/25798#issuecomment-562214053), we can see that the results are less noisy now. These results support no noticeable regressions in speed. Test Plan: - wait for CI Differential Revision: D18858543 Pulled By: zou3519 fbshipit-source-id: 08bf3853a9f506c6b084808dc9ddd1e835f48c13
102 lines
3.3 KiB
C++
102 lines
3.3 KiB
C++
#include <torch/csrc/python_dimname.h>
|
|
#include <torch/csrc/Exceptions.h>
|
|
#include <torch/csrc/utils/python_strings.h>
|
|
#include <c10/util/flat_hash_map.h>
|
|
#include <ATen/core/EnableNamedTensor.h>
|
|
|
|
namespace torch {
|
|
|
|
struct InternedStringsTable {
|
|
InternedStringsTable() = default;
|
|
~InternedStringsTable();
|
|
InternedStringsTable(const InternedStringsTable &) = delete;
|
|
InternedStringsTable& operator =(InternedStringsTable const&) = delete;
|
|
InternedStringsTable(InternedStringsTable&&) = delete;
|
|
InternedStringsTable& operator=(InternedStringsTable&&) = delete;
|
|
|
|
at::optional<at::Dimname> lookup(PyObject* obj);
|
|
// Precondition: obj is an interned python string.
|
|
void addMapping(PyObject* obj, at::Dimname dimname);
|
|
private:
|
|
ska::flat_hash_map<PyObject*,at::Dimname> py_interned_string_to_dimname_;
|
|
};
|
|
|
|
InternedStringsTable kPyInternedStringToDimname;
|
|
|
|
InternedStringsTable::~InternedStringsTable() {
|
|
for (auto it = py_interned_string_to_dimname_.begin();
|
|
it != py_interned_string_to_dimname_.end(); ++it) {
|
|
// See Note [References to python interned strings]
|
|
Py_DECREF(it->first);
|
|
}
|
|
}
|
|
|
|
at::optional<at::Dimname> InternedStringsTable::lookup(PyObject* obj) {
|
|
auto it = py_interned_string_to_dimname_.find(obj);
|
|
if (it == py_interned_string_to_dimname_.end()) {
|
|
return at::nullopt;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
|
|
void InternedStringsTable::addMapping(PyObject* obj, at::Dimname dimname) {
|
|
// Note [References to python interned strings]
|
|
// If a Python interned string has no references to it, then it gets
|
|
// deallocated, invalidating this mapping. Let's immortalize the string by
|
|
// holding a refcount to it and releasing it in the destructor
|
|
Py_INCREF(obj);
|
|
py_interned_string_to_dimname_.emplace(obj, dimname);
|
|
}
|
|
|
|
} // namespace torch
|
|
|
|
bool THPUtils_checkDimname(PyObject* obj) {
|
|
return obj == Py_None || THPUtils_checkString(obj);
|
|
}
|
|
|
|
// To avoid ambiguity with IntArrayRef, we parse obj as a DimnameList if
|
|
// it is a list or tuple and its first elt is a Dimname
|
|
bool THPUtils_checkDimnameList(PyObject* obj) {
|
|
auto tuple = PyTuple_Check(obj);
|
|
if (!tuple && !PyList_Check(obj)) {
|
|
return false;
|
|
}
|
|
auto size = tuple ? PyTuple_GET_SIZE(obj) : PyList_GET_SIZE(obj);
|
|
if (size == 0) {
|
|
return true;
|
|
}
|
|
PyObject* first_elt = tuple ? PyTuple_GET_ITEM(obj, 0) : PyList_GET_ITEM(obj, 0);
|
|
return THPUtils_checkDimname(first_elt);
|
|
}
|
|
|
|
at::Dimname THPDimname_parse(PyObject* obj) {
|
|
if (obj == Py_None) {
|
|
return at::Dimname::wildcard();
|
|
}
|
|
|
|
if (!THPUtils_checkString(obj)) {
|
|
throw torch::TypeError("expected None or string for Dimname but got %s", Py_TYPE(obj)->tp_name);
|
|
}
|
|
|
|
if (!THPUtils_isInterned(obj)) {
|
|
// internStringInPlace decrefs obj and increfs the result. Because we're
|
|
// not actually returning the result to the user, we need to undo these.
|
|
// See https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_InternInPlace
|
|
Py_INCREF(obj);
|
|
THPUtils_internStringInPlace(&obj);
|
|
Py_DECREF(obj);
|
|
}
|
|
|
|
auto maybeDimname = torch::kPyInternedStringToDimname.lookup(obj);
|
|
if (maybeDimname) {
|
|
return *maybeDimname;
|
|
}
|
|
|
|
const auto name = THPUtils_unpackString(obj);
|
|
auto dimname = at::Dimname::fromSymbol(at::Symbol::dimname(name));
|
|
torch::kPyInternedStringToDimname.addMapping(obj, dimname);
|
|
return dimname;
|
|
}
|
|
|