Improve TypeMeta (#11502)

Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/11502

TypeMeta now is only a pointer to a TypeMetaData structure, of which there is exactly one global instance per type.
This reduces the size of everything storing a TypeMeta (Tensor, Blob, ...) and potentially improves performance.

Also, this diff gets rid of the type name registry in favor of static strings.

Experiments (summary: 1-3% perf gain)
- Service Lab: https://our.intern.facebook.com/intern/servicelab/30712497/
 -> No significant results found.
- Mobile Lab c10bench.json: https://our.intern.facebook.com/intern/fblearner/details/75984908/
 -> 1-3% perf gain
- Mobile Lab c10bench default: https://our.intern.facebook.com/intern/fblearner/details/75984999/
 -> 2-3% perf gain
- adindexer canary: https://our.intern.facebook.com/intern/ads/canary/413002142824203076
 -> no significant changes (benchmark too noisy)
- adfinder canary: https://our.intern.facebook.com/intern/ads/canary/413002166737860362
 -> no significant changes (benchmark too noisy)

Reviewed By: dzhulgakov

Differential Revision: D9763422

fbshipit-source-id: fc08937f114af5ff9f3ddbe7c7e396942868cdf5
This commit is contained in:
Sebastian Messmer 2018-10-06 14:00:15 -07:00 committed by Facebook Github Bot
parent ac9bb8ecef
commit 6f664d3917
7 changed files with 434 additions and 347 deletions

View File

@ -69,8 +69,9 @@ enum class ScalarType : int8_t {
};
static inline DataType scalarTypeToDataType(ScalarType scalar_type) {
#define DEFINE_CASE(ctype,name,_) \
case ScalarType:: name : return caffe2::TypeMeta::Id<ctype>();
#define DEFINE_CASE(ctype, name, _) \
case ScalarType::name: \
return caffe2::TypeIdentifier::Get<ctype>();
switch(scalar_type) {
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX(DEFINE_CASE)
@ -93,9 +94,9 @@ static inline caffe2::TypeMeta scalarTypeToTypeMeta(ScalarType scalar_type) {
}
static inline ScalarType dataTypeToScalarType(DataType dtype) {
#define DEFINE_IF(ctype,name,_) \
if (dtype == caffe2::TypeMeta::Id<ctype>()) { \
return ScalarType:: name; \
#define DEFINE_IF(ctype, name, _) \
if (dtype == caffe2::TypeIdentifier::Get<ctype>()) { \
return ScalarType::name; \
}
AT_FORALL_SCALAR_TYPES_WITH_COMPLEX(DEFINE_IF)
#undef DEFINE_IF

View File

@ -10,28 +10,24 @@
using std::string;
namespace caffe2 {
std::unordered_map<TypeIdentifier, string>& gTypeNames() {
static std::unordered_map<TypeIdentifier, string> g_type_names;
return g_type_names;
}
std::unordered_set<string>& gRegisteredTypeNames() {
static std::unordered_set<string> g_registered_type_names;
return g_registered_type_names;
}
std::mutex& gTypeRegistrationMutex() {
static std::mutex g_type_registration_mutex;
return g_type_registration_mutex;
}
void TypeMeta::_ThrowRuntimeTypeLogicError(const std::string& msg) {
namespace detail {
C10_EXPORT void _ThrowRuntimeTypeLogicError(const string& msg) {
// In earlier versions it used to be std::abort() but it's a bit hard-core
// for a library
AT_ERROR(msg);
}
const TypeMetaData _typeMetaDataInstance_uninitialized_ = detail::TypeMetaData(0, nullptr, nullptr, nullptr, TypeIdentifier::uninitialized(), "nullptr (uninitialized)");
} // namespace detail
// TODO Inlineable on non-MSVC like other preallocated ids?
template<>
C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<detail::_Uninitialized>() noexcept {
return &detail::_typeMetaDataInstance_uninitialized_;
}
TypeIdentifier TypeIdentifier::createTypeId() {
static std::atomic<TypeIdentifier::underlying_type> counter(
TypeMeta::Id<_CaffeHighestPreallocatedTypeId>().underlyingId());
@ -44,46 +40,39 @@ TypeIdentifier TypeIdentifier::createTypeId() {
return TypeIdentifier(new_value);
}
CAFFE_DEFINE_KNOWN_TYPE(float);
CAFFE_DEFINE_KNOWN_TYPE(int);
CAFFE_DEFINE_KNOWN_TYPE(std::string);
CAFFE_DEFINE_KNOWN_TYPE(bool);
CAFFE_DEFINE_KNOWN_TYPE(uint8_t);
CAFFE_DEFINE_KNOWN_TYPE(int8_t);
CAFFE_DEFINE_KNOWN_TYPE(uint16_t);
CAFFE_DEFINE_KNOWN_TYPE(int16_t);
CAFFE_DEFINE_KNOWN_TYPE(int64_t);
CAFFE_DEFINE_KNOWN_TYPE(double);
CAFFE_DEFINE_KNOWN_TYPE(char);
CAFFE_DEFINE_KNOWN_TYPE(at::Half);
CAFFE_DEFINE_KNOWN_TYPE(std::unique_ptr<std::mutex>);
CAFFE_DEFINE_KNOWN_TYPE(std::unique_ptr<std::atomic<bool>>);
CAFFE_DEFINE_KNOWN_TYPE(std::vector<int32_t>);
CAFFE_DEFINE_KNOWN_TYPE(std::vector<int64_t>);
CAFFE_DEFINE_KNOWN_TYPE(std::vector<unsigned long>);
CAFFE_DEFINE_KNOWN_TYPE(bool*);
CAFFE_DEFINE_KNOWN_TYPE(char*);
CAFFE_DEFINE_KNOWN_TYPE(int*);
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(0, uint8_t)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(1, int8_t)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(2, int16_t)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(3, int)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(4, int64_t)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(5, at::Half)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(6, float)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(7, double)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(8, at::ComplexHalf)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(9, std::complex<float>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(10, std::complex<double>)
// 11 = undefined type id
// 12 = Tensor (defined in tensor.cc)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(13, std::string)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(14, bool)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(15, uint16_t)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(16, char)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(17, std::unique_ptr<std::mutex>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(18, std::unique_ptr<std::atomic<bool>>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(19, std::vector<int32_t>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(20, std::vector<int64_t>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(21, std::vector<unsigned long>)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(22, bool*)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(23, char*)
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(24, int*)
// see typeid.h for details.
#if defined(_MSC_VER) || defined(__APPLE__) || \
(defined(__ANDROID__) && !defined(__LP64__))
CAFFE_DEFINE_KNOWN_TYPE(long);
CAFFE_DEFINE_KNOWN_TYPE(std::vector<long>);
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(25, long);
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(26, std::vector<long>);
#endif
CAFFE_DEFINE_KNOWN_TYPE(_CaffeHighestPreallocatedTypeId);
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(27, _CaffeHighestPreallocatedTypeId)
namespace {
// This single registerer exists solely for us to be able to name a TypeMeta
// for unintializied blob. You should not use this struct yourself - it is
// intended to be only instantiated once here.
struct UninitializedTypeNameRegisterer {
UninitializedTypeNameRegisterer() {
gTypeNames()[TypeIdentifier::uninitialized()] = "nullptr (uninitialized)";
}
};
static UninitializedTypeNameRegisterer g_uninitialized_type_name_registerer;
} // namespace
} // namespace caffe2

View File

@ -17,18 +17,29 @@
#include <exception>
#include "ATen/core/Error.h"
#include "caffe2/core/macros.h"
#include "ATen/core/Backtrace.h"
#include "ATen/core/Macros.h"
#include "ATen/core/C++17.h"
#include "ATen/core/Error.h"
#include "ATen/core/Half.h"
#include "ATen/core/IdWrapper.h"
#include "ATen/core/Macros.h"
/*
* TypeIdentifier is a small type containing an id.
* Types must be registered using CAFFE_KNOWN_TYPE() for them to have a type id.
* If a type is registered, you can also create an object containing meta data
* like constructor, destructor, stringified name, ... about the type by calling
* TypeMeta::Make<T>. This returns a TypeMeta() object, which is basically just
* a pointer to the type information, so it's cheap to pass around.
*/
// TODO: This file is still in the caffe2 namespace, despite living
// in the ATen directory. This is because the macro CAFFE_DECLARE_KNOWN_TYPE
// defines a template specialization, which relies on the namespace of TypeMeta
// matching the namespace where the macro is called. This requires us to
// fix all of the call-sites, which I want to do later. So the namespace
// is not fixed at the moment.
// in the ATen directory. This is because the macro
// CAFFE_KNOWN_TYPE defines a template specialization, which relies
// on the namespace of TypeMeta matching the namespace where the macro is
// called. This requires us to fix all of the call-sites, which I want to do
// later. So the namespace is not fixed at the moment.
// Make at::Half a fundamental type.
namespace std {
@ -39,8 +50,6 @@ struct is_fundamental<at::Half> : std::true_type {
namespace caffe2 {
class TypeMeta;
/**
* A type id is a unique id for a given C++ type.
* You need to register your types using CAFFE_KNOWN_TYPE(MyType) to be able to
@ -62,7 +71,15 @@ class CAFFE2_API TypeIdentifier final
return TypeIdentifier(11);
}
const char* name() const noexcept;
/**
* Returns the unique id for the given type T. The id is unique for the type T
* in the sense that for any two different types, their ids are different; for
* the same type T, the id remains the same over different calls of the
* function. However, this is not guaranteed over different runs, as the id
* is generated during run-time. Do NOT serialize the id for storage.
*/
template <typename T>
CAFFE2_API static TypeIdentifier Get();
private:
constexpr explicit TypeIdentifier(uint16_t id) : IdWrapper(id) {}
@ -91,55 +108,193 @@ AT_DEFINE_HASH_FOR_IDWRAPPER(caffe2::TypeIdentifier)
namespace caffe2 {
CAFFE2_API std::unordered_map<TypeIdentifier, std::string>& gTypeNames();
CAFFE2_API std::unordered_set<std::string>& gRegisteredTypeNames();
namespace detail {
inline const char* TypeIdentifier::name() const noexcept {
auto it = gTypeNames().find(*this);
assert(it != gTypeNames().end());
return it->second.c_str();
// This struct holds the actual type information. There will be
// one allocated per type. TypeMeta objects will then point to the struct
// instance for the type they're configured for.
struct TypeMetaData final {
using PlacementNew = void(void*, size_t);
using TypedCopy = void(const void*, void*, size_t);
using TypedDestructor = void(void*, size_t);
TypeMetaData() = delete;
constexpr TypeMetaData(
size_t itemsize,
PlacementNew* ctor,
TypedCopy* copy,
TypedDestructor* dtor,
TypeIdentifier id,
const char* name) noexcept
: itemsize_(itemsize), ctor_(ctor), copy_(copy), dtor_(dtor), id_(id), name_(name) {}
size_t itemsize_;
PlacementNew* ctor_;
TypedCopy* copy_;
TypedDestructor* dtor_;
TypeIdentifier id_;
const char* name_;
};
// Mechanism for throwing errors which can't be prevented at compile time
// due to type erasure. E.g. somebody calling TypeMeta::copy() for
// non-copyable type. Right now just throws exception but is implemented
// in .cpp to manage dependencies
CAFFE2_API void _ThrowRuntimeTypeLogicError(const std::string& msg);
/**
* Placement new function for the type.
*/
template <typename T>
inline void _Ctor(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) {
new (typed_ptr + i) T;
}
}
CAFFE2_API std::mutex& gTypeRegistrationMutex();
template <typename T>
struct TypeNameRegisterer {
TypeNameRegisterer(TypeIdentifier id, const std::string& literal_name) {
std::lock_guard<std::mutex> guard(gTypeRegistrationMutex());
#ifdef __GXX_RTTI
(void)literal_name;
inline void _CtorNotDefault(void* /*ptr*/, size_t /*n*/) {
_ThrowRuntimeTypeLogicError(
"Type " + std::string(at::demangle_type<T>()) +
" is not default-constructible.");
}
std::string name = at::demangle(typeid(T).name());
// If we are in RTTI mode, we will also use this opportunity to do sanity
// check if there are duplicated ids registered for the same type. This
// usually happens when one does not do RTLD_GLOBAL, which is often the
// case in Python. The way we do the check is to make sure that there are
// no duplicated names registered - this could be done by checking the
// uniqueness of names.
if (gRegisteredTypeNames().count(name)) {
AT_ERROR("typeid.h: Type name ", name, " was registered twice. "
"This should not happen. Things to check:\n"
"1. Did you add a new CAFFE_KNOWN_TYPE? If so, check that "
"it is not duplicated with an existing CAFFE_KNOWN_TYPE.\n"
"2. Did you build and install PyTorch and Caffe2 separately? "
"For example, this would be the case if you ran scripts/onnx/install.sh or "
"scripts/onnx/install-develop.sh prior to Aug 12, 2018 "
"(commit 1756daaa7530d). If so, rebuild using the environment variable "
" FULL_CAFFE2=1 (if you build latest master, the ONNX scripts are "
"updated to do this for you.) "
"For more context, see https://github.com/pytorch/pytorch/issues/10460");
}
gRegisteredTypeNames().insert(name);
gTypeNames()[id] = name;
#else // __GXX_RTTI
if (literal_name.empty()) {
gTypeNames()[id] = "(RTTI disabled, cannot show name)";
} else {
gTypeNames()[id] = literal_name;
}
#endif // __GXX_RTTI
template<
typename T,
c10::guts::enable_if_t<
std::is_fundamental<T>::value || std::is_pointer<T>::value>* = nullptr>
inline constexpr TypeMetaData::PlacementNew* _PickCtor() {
return nullptr;
}
template <
typename T,
c10::guts::enable_if_t<
!(std::is_fundamental<T>::value || std::is_pointer<T>::value) &&
std::is_default_constructible<T>::value
>* = nullptr>
inline constexpr TypeMetaData::PlacementNew* _PickCtor() {
return &_Ctor<T>;
}
template <
typename T,
c10::guts::enable_if_t<
!(std::is_fundamental<T>::value || std::is_pointer<T>::value) &&
!std::is_default_constructible<T>::value
>* = nullptr>
inline constexpr TypeMetaData::PlacementNew* _PickCtor() {
return &_CtorNotDefault<T>;
}
/**
* Typed copy function for classes.
*/
template <typename T>
inline void _Copy(const void* src, void* dst, size_t n) {
const T* typed_src = static_cast<const T*>(src);
T* typed_dst = static_cast<T*>(dst);
for (size_t i = 0; i < n; ++i) {
typed_dst[i] = typed_src[i];
}
};
}
/**
* A placeholder function for types that do not allow assignment.
*/
template <typename T>
inline void _CopyNotAllowed(const void* /*src*/, void* /*dst*/, size_t /*n*/) {
_ThrowRuntimeTypeLogicError(
"Type " + std::string(at::demangle_type<T>()) +
" does not allow assignment.");
}
template<
typename T,
c10::guts::enable_if_t<std::is_fundamental<T>::value || std::is_pointer<T>::value>* = nullptr
>
inline constexpr TypeMetaData::TypedCopy* _PickCopy() {
return nullptr;
}
template <
typename T,
c10::guts::enable_if_t<
!(std::is_fundamental<T>::value || std::is_pointer<T>::value) &&
std::is_copy_assignable<T>::value
>* = nullptr>
inline constexpr TypeMetaData::TypedCopy* _PickCopy() {
return &_Copy<T>;
}
template <
typename T,
c10::guts::enable_if_t<
!(std::is_fundamental<T>::value || std::is_pointer<T>::value) &&
!std::is_copy_assignable<T>::value
>* = nullptr>
inline constexpr TypeMetaData::TypedCopy* _PickCopy() {
return &_CopyNotAllowed<T>;
}
/**
* Destructor for non-fundamental types.
*/
template <typename T>
inline void _Dtor(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) {
typed_ptr[i].~T();
}
}
template<
typename T,
c10::guts::enable_if_t<std::is_fundamental<T>::value || std::is_pointer<T>::value>* = nullptr
>
inline constexpr TypeMetaData::TypedDestructor* _PickDtor() {
return nullptr;
}
template<
typename T,
c10::guts::enable_if_t<!(std::is_fundamental<T>::value || std::is_pointer<T>::value)>* = nullptr
>
inline constexpr TypeMetaData::TypedDestructor* _PickDtor() {
return &_Dtor<T>;
}
template <class T>
const char* __TypeName() noexcept;
template <class T>
const char* _TypeName() noexcept {
static const char* literal_name = __TypeName<T>();
#ifdef __GXX_RTTI
std::ignore = literal_name; // suppress unused warning
static const std::string name = at::demangle(typeid(T).name());
return name.c_str();
#else
return literal_name;
#endif
}
template<class T>
inline TypeMetaData _makeTypeMetaDataInstance() {
return {
sizeof(T),
_PickCtor<T>(),
_PickCopy<T>(),
_PickDtor<T>(),
TypeIdentifier::Get<T>(),
_TypeName<T>()
};
}
class _Uninitialized final {};
} // namespace detail
/**
* TypeMeta is a thin class that allows us to store the type of a container such
@ -149,243 +304,127 @@ struct TypeNameRegisterer {
*/
class CAFFE2_API TypeMeta {
public:
using PlacementNew = void(void*, size_t);
using TypedCopy = void(const void*, void*, size_t);
using TypedDestructor = void(void*, size_t);
using PlacementNew = detail::TypeMetaData::PlacementNew;
using TypedCopy = detail::TypeMetaData::TypedCopy;
using TypedDestructor = detail::TypeMetaData::TypedDestructor;
/** Create a dummy TypeMeta object. To create a TypeMeta object for a specific
* type, use TypeMeta::Make<T>().
*/
TypeMeta() noexcept
: id_(TypeIdentifier::uninitialized()),
itemsize_(0),
ctor_(nullptr),
copy_(nullptr),
dtor_(nullptr) {}
TypeMeta() noexcept;
/**
* Copy constructor.
*/
TypeMeta(const TypeMeta& src) noexcept = default;
constexpr TypeMeta(const TypeMeta& src) noexcept = default;
/**
* Assignment operator.
*/
TypeMeta& operator=(const TypeMeta& src) noexcept = default;
AT_CPP14_CONSTEXPR TypeMeta& operator=(const TypeMeta& src) noexcept =
default;
TypeMeta(TypeMeta&& rhs) noexcept = default;
constexpr TypeMeta(TypeMeta&& rhs) noexcept = default;
private:
// TypeMeta can only be created by Make, making sure that we do not
// create incorrectly mixed up TypeMeta objects.
TypeMeta(
TypeIdentifier i,
size_t s,
PlacementNew* ctor,
TypedCopy* copy,
TypedDestructor* dtor) noexcept
: id_(i), itemsize_(s), ctor_(ctor), copy_(copy), dtor_(dtor) {}
// Mechanism for throwing errors which can't be prevented at compile time
// due to type erasure. E.g. somebody calling TypeMeta::copy() for
// non-copiable type. Right now just throws exception but is implemented
// in .cpp to manage dependencies
static void _ThrowRuntimeTypeLogicError(const std::string& msg);
constexpr TypeMeta(const detail::TypeMetaData* data) noexcept : data_(data) {}
public:
/**
* Returns the type id.
*/
const TypeIdentifier& id() const noexcept {
return id_;
constexpr TypeIdentifier id() const noexcept {
return data_->id_;
}
/**
* Returns the size of the item.
*/
const size_t& itemsize() const noexcept {
return itemsize_;
constexpr size_t itemsize() const noexcept {
return data_->itemsize_;
}
/**
* Returns the placement new function pointer for individual items.
*/
PlacementNew* ctor() const noexcept {
return ctor_;
constexpr PlacementNew* ctor() const noexcept {
return data_->ctor_;
}
/**
* Returns the typed copy function pointer for individual iterms.
*/
TypedCopy* copy() const noexcept {
return copy_;
constexpr TypedCopy* copy() const noexcept {
return data_->copy_;
}
/**
* Returns the destructor function pointer for individual items.
*/
TypedDestructor* dtor() const noexcept {
return dtor_;
constexpr TypedDestructor* dtor() const noexcept {
return data_->dtor_;
}
/**
* Returns a printable name for the type.
*/
const char* name() const noexcept {
auto it = gTypeNames().find(id_);
assert(it != gTypeNames().end());
return it->second.c_str();
constexpr const char* name() const noexcept {
return data_->name_;
}
friend bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept;
template <typename T>
bool Match() const noexcept {
return (id_ == Id<T>());
constexpr bool Match() const noexcept {
return (*this == Make<T>());
}
// Below are static functions that can be called by passing a specific type.
/**
* Returns the unique id for the given type T. The id is unique for the type T
* in the sense that for any two different types, their id are different; for
* the same type T, the id remains the same over different calls of the
* function. However, this is not guaranteed over different runs, as the id
* is generated during run-time. Do NOT serialize the id for storage.
*/
template <typename T>
CAFFE2_API static TypeIdentifier Id();
template <class T>
static TypeIdentifier Id() noexcept {
return TypeIdentifier::Get<T>();
}
/**
* Returns the item size of the type. This is equivalent to sizeof(T).
*/
template <typename T>
static size_t ItemSize() {
template <class T>
static const char* TypeName() noexcept {
return Make<T>().name();
}
template <class T>
static constexpr size_t ItemSize() noexcept {
return sizeof(T);
}
/**
* Returns the registered printable name of the type.
*
* Works for only the ones registered with CAFFE_KNOWN_TYPE
*/
template <typename T>
static const char* TypeName() {
auto it = gTypeNames().find(Id<T>());
assert(it != gTypeNames().end());
return it->second.c_str();
}
/**
* Placement new function for the type.
*/
template <typename T>
static void _Ctor(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) {
new (typed_ptr + i) T;
}
}
template <typename T>
static void _CtorNotDefault(void* /*ptr*/, size_t /*n*/) {
_ThrowRuntimeTypeLogicError(
"Type " + std::string(at::demangle_type<T>()) +
" is not default-constructible.");
}
template <
typename T,
typename std::enable_if<std::is_default_constructible<T>::value>::type* =
nullptr>
static inline PlacementNew* _PickCtor() {
return _Ctor<T>;
}
template <
typename T,
typename std::enable_if<!std::is_default_constructible<T>::value>::type* =
nullptr>
static inline PlacementNew* _PickCtor() {
return _CtorNotDefault<T>;
}
/**
* Typed copy function for classes.
*/
template <typename T>
static void _Copy(const void* src, void* dst, size_t n) {
const T* typed_src = static_cast<const T*>(src);
T* typed_dst = static_cast<T*>(dst);
for (size_t i = 0; i < n; ++i) {
typed_dst[i] = typed_src[i];
}
}
/**
* A placeholder function for types that do not allow assignment.
*/
template <typename T>
static void _CopyNotAllowed(
const void* /*src*/,
void* /*dst*/,
size_t /*n*/) {
_ThrowRuntimeTypeLogicError(
"Type " + std::string(at::demangle_type<T>()) +
" does not allow assignment.");
}
template <
typename T,
typename std::enable_if<std::is_copy_assignable<T>::value>::type* =
nullptr>
static inline TypedCopy* _PickCopy() {
return _Copy<T>;
}
template <
typename T,
typename std::enable_if<!std::is_copy_assignable<T>::value>::type* =
nullptr>
static inline TypedCopy* _PickCopy() {
return _CopyNotAllowed<T>;
}
/**
* Destructor for non-fundamental types.
*/
template <typename T>
static void _Dtor(void* ptr, size_t n) {
T* typed_ptr = static_cast<T*>(ptr);
for (size_t i = 0; i < n; ++i) {
typed_ptr[i].~T();
}
}
/**
* Returns a TypeMeta object that corresponds to the typename T.
*/
template <typename T>
static typename std::enable_if<
std::is_fundamental<T>::value || std::is_pointer<T>::value,
TypeMeta>::type
Make() {
return TypeMeta(Id<T>(), ItemSize<T>(), nullptr, nullptr, nullptr);
}
template <typename T>
static typename std::enable_if<
!(std::is_fundamental<T>::value || std::is_pointer<T>::value),
TypeMeta>::type
Make() {
return TypeMeta(
Id<T>(), ItemSize<T>(), _PickCtor<T>(), _PickCopy<T>(), _Dtor<T>);
static TypeMeta Make() {
// The instance pointed to is declared here, but defined in a .cpp file.
// We need to silence the compiler warning about using an undefined
// variable template. '-Wpragmas' and '-Wunknown-warning-option' has to be
// disabled for compilers that don't know '-Wundefined-var-template' and
// would error at our attempt to disable it.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#pragma GCC diagnostic ignored "-Wundefined-var-template"
return TypeMeta(_typeMetaDataInstance<T>());
#pragma GCC diagnostic pop
}
private:
TypeIdentifier id_;
size_t itemsize_;
PlacementNew* ctor_;
TypedCopy* copy_;
TypedDestructor* dtor_;
const detail::TypeMetaData* data_;
template<class T>
CAFFE2_API static const detail::TypeMetaData* _typeMetaDataInstance() noexcept;
};
template<>
C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<detail::_Uninitialized>() noexcept;
inline TypeMeta::TypeMeta() noexcept : data_(_typeMetaDataInstance<detail::_Uninitialized>()) {}
inline bool operator==(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
return (lhs.id_ == rhs.id_);
return (lhs.data_ == rhs.data_);
}
inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
return !operator==(lhs, rhs);
@ -395,10 +434,10 @@ inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
* Register unique id for a type so it can be used in TypeMeta context, e.g. be
* used as a type for Blob or for Tensor elements.
*
* CAFFE_KNOWN_TYPE does explicit instantiation of TypeMeta::Id<T> template
* function and thus needs to be put in a single translation unit (.cpp file)
* for a given type T. Other translation units that use type T as a type of the
* caffe2::Blob or element type of caffe2::Tensor need to depend on the
* CAFFE_KNOWN_TYPE does explicit instantiation of TypeIdentifier::Get<T>
* template function and thus needs to be put in a single translation unit (.cpp
* file) for a given type T. Other translation units that use type T as a type
* of the caffe2::Blob or element type of caffe2::Tensor need to depend on the
* translation unit that contains CAFFE_KNOWN_TYPE declaration via regular
* linkage dependencies.
*
@ -411,86 +450,140 @@ inline bool operator!=(const TypeMeta& lhs, const TypeMeta& rhs) noexcept {
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51930
// and as a result, we define these two macros slightly differently.
#if defined(_MSC_VER) || defined(__clang__)
#define _CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, Counter) \
namespace detail { \
const TypeMetaData MACRO_CONCAT(_typeMetaDataInstance_, Counter) = \
_makeTypeMetaDataInstance<T>(); \
} \
template<> \
C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
return &MACRO_CONCAT(detail::_typeMetaDataInstance_, Counter); \
}
#define CAFFE_KNOWN_TYPE(T) \
template <> \
C10_EXPORT TypeIdentifier TypeMeta::Id<T>() { \
C10_EXPORT TypeIdentifier TypeIdentifier::Get<T>() { \
static const TypeIdentifier type_id = TypeIdentifier::createTypeId(); \
static TypeNameRegisterer<T> registerer(type_id, #T); \
return type_id; \
}
} \
namespace detail { \
template <> \
C10_EXPORT const char* __TypeName<T>() noexcept { \
return #T; \
} \
} \
_CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, __COUNTER__)
#else // defined(_MSC_VER) || defined(__clang__)
#define _CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, Counter) \
namespace detail { \
const TypeMetaData MACRO_CONCAT(_typeMetaDataInstance_, Counter) = \
_makeTypeMetaDataInstance<T>(); \
} \
template<> \
const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
return &MACRO_CONCAT(detail::_typeMetaDataInstance_, Counter); \
}
#define CAFFE_KNOWN_TYPE(T) \
template <> \
TypeIdentifier TypeMeta::Id<T>() { \
TypeIdentifier TypeIdentifier::Get<T>() { \
static const TypeIdentifier type_id = TypeIdentifier::createTypeId(); \
static TypeNameRegisterer<T> registerer(type_id, #T); \
return type_id; \
}
} \
namespace detail { \
template <> \
const char* __TypeName<T>() noexcept { \
return #T; \
} \
} \
_CAFFE_KNOWN_TYPE_DEFINE_TYPEMETADATA_INSTANCE(T, __COUNTER__)
#endif // defined(_MSC_VER) || defined(__clang__)
/**
* CAFFE_DECLARE_KNOWN_TYPE and CAFFE_DEFINE_KNOWN_TYPE are used
* CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE is used
* to preallocate ids for types that are queried very often so that they
* can be resolved at compile time. Please use CAFFE_KNOWN_TYPE() instead
* for your own types to allocate dynamic ids for them.
*/
#ifdef _MSC_VER
#define CAFFE_DECLARE_KNOWN_TYPE(PreallocatedId, T) \
template <> \
inline CAFFE2_API TypeIdentifier TypeMeta::Id<T>() { \
return TypeIdentifier(PreallocatedId); \
#define CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
template <> \
inline C10_EXPORT TypeIdentifier TypeIdentifier::Get<T>() { \
return TypeIdentifier(PreallocatedId); \
} \
namespace detail { \
template <> \
inline C10_EXPORT const char* __TypeName<T>() noexcept { \
return #T; \
} \
}
#define CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(Id, T) \
namespace detail { \
const TypeMetaData \
MACRO_CONCAT(_typeMetaDataInstance_preallocated_, Id) \
= _makeTypeMetaDataInstance<T>(); \
} \
template<> \
C10_EXPORT const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
return &MACRO_CONCAT(detail::_typeMetaDataInstance_preallocated_, Id); \
}
#else // _MSC_VER
#define CAFFE_DECLARE_KNOWN_TYPE(PreallocatedId, T) \
template <> \
inline TypeIdentifier TypeMeta::Id<T>() { \
return TypeIdentifier(PreallocatedId); \
#define _CAFFE_KNOWN_TYPE_DEFINE_PREALLOCATED_TYPEMETADATA_INSTANCE(Id, T) \
namespace detail { \
C10_EXPORT extern const TypeMetaData \
MACRO_CONCAT(_typeMetaDataInstance_preallocated_, Id); \
} \
template<> \
inline const detail::TypeMetaData* TypeMeta::_typeMetaDataInstance<T>() noexcept { \
return &MACRO_CONCAT(detail::_typeMetaDataInstance_preallocated_, Id); \
}
#define CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(PreallocatedId, T) \
template <> \
inline C10_EXPORT TypeIdentifier TypeIdentifier::Get<T>() { \
return TypeIdentifier(PreallocatedId); \
} \
namespace detail { \
template <> \
inline C10_EXPORT const char* __TypeName<T>() noexcept { \
return #T; \
} \
} \
_CAFFE_KNOWN_TYPE_DEFINE_PREALLOCATED_TYPEMETADATA_INSTANCE(PreallocatedId, T)
#define CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(Id, T) \
namespace detail { \
const TypeMetaData MACRO_CONCAT(_typeMetaDataInstance_preallocated_, Id) \
= _makeTypeMetaDataInstance<T>(); \
}
#endif
#define CONCAT_IMPL(x, y) x##y
#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
#define CAFFE_DEFINE_KNOWN_TYPE(T) \
namespace { \
TypeNameRegisterer<T> MACRO_CONCAT(registerer, __COUNTER__)( \
TypeMeta::Id<T>(), \
#T); \
}
class Tensor;
// Note: we have preallocated the numbers so they line up exactly
// with at::ScalarType's numbering. All other numbers do not matter.
struct _CaffeHighestPreallocatedTypeId final {};
CAFFE_DECLARE_KNOWN_TYPE(0, uint8_t)
CAFFE_DECLARE_KNOWN_TYPE(1, int8_t)
CAFFE_DECLARE_KNOWN_TYPE(2, int16_t)
CAFFE_DECLARE_KNOWN_TYPE(3, int)
CAFFE_DECLARE_KNOWN_TYPE(4, int64_t)
CAFFE_DECLARE_KNOWN_TYPE(5, at::Half)
CAFFE_DECLARE_KNOWN_TYPE(6, float)
CAFFE_DECLARE_KNOWN_TYPE(7, double)
CAFFE_DECLARE_KNOWN_TYPE(8, at::ComplexHalf)
CAFFE_DECLARE_KNOWN_TYPE(9, std::complex<float>)
CAFFE_DECLARE_KNOWN_TYPE(10, std::complex<double>)
// TODO static_assert number of declare/define align
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(0, uint8_t)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(1, int8_t)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(2, int16_t)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(3, int)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(4, int64_t)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(5, at::Half)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(6, float)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(7, double)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(8, at::ComplexHalf)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(9, std::complex<float>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(10, std::complex<double>)
// 11 = undefined type id
CAFFE_DECLARE_KNOWN_TYPE(12, Tensor)
CAFFE_DECLARE_KNOWN_TYPE(13, std::string)
CAFFE_DECLARE_KNOWN_TYPE(14, bool)
CAFFE_DECLARE_KNOWN_TYPE(15, uint16_t)
CAFFE_DECLARE_KNOWN_TYPE(16, char)
CAFFE_DECLARE_KNOWN_TYPE(17, std::unique_ptr<std::mutex>)
CAFFE_DECLARE_KNOWN_TYPE(18, std::unique_ptr<std::atomic<bool>>)
CAFFE_DECLARE_KNOWN_TYPE(19, std::vector<int32_t>)
CAFFE_DECLARE_KNOWN_TYPE(20, std::vector<int64_t>)
CAFFE_DECLARE_KNOWN_TYPE(21, std::vector<unsigned long>)
CAFFE_DECLARE_KNOWN_TYPE(22, bool*)
CAFFE_DECLARE_KNOWN_TYPE(23, char*)
CAFFE_DECLARE_KNOWN_TYPE(24, int*)
// 12 = Tensor (defined in tensor.h)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(13, std::string)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(14, bool)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(15, uint16_t)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(16, char)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(17, std::unique_ptr<std::mutex>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(18, std::unique_ptr<std::atomic<bool>>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(19, std::vector<int32_t>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(20, std::vector<int64_t>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(21, std::vector<unsigned long>)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(22, bool*)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(23, char*)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(24, int*)
// For some of the compilers, long is definied separately from int32_t and
// int64_t. As a result we will need to actually define them separately.
@ -498,9 +591,9 @@ CAFFE_DECLARE_KNOWN_TYPE(24, int*)
// explicitly. Explicit long type annotation may go away in the future.
#if defined(_MSC_VER) || defined(__APPLE__) || \
(defined(__ANDROID__) && !defined(__LP64__))
CAFFE_DECLARE_KNOWN_TYPE(25, long)
CAFFE_DECLARE_KNOWN_TYPE(26, std::vector<long>)
#endif
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(25, long)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(26, std::vector<long>)
#endif
CAFFE_DECLARE_KNOWN_TYPE(27, _CaffeHighestPreallocatedTypeId)
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(27, _CaffeHighestPreallocatedTypeId)
} // namespace caffe2

View File

@ -29,4 +29,7 @@
classname(const classname&) = delete; \
classname& operator=(const classname&) = delete
#define CONCAT_IMPL(x, y) x##y
#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
#endif // C10_MACROS_MACROS_H_

View File

@ -4,7 +4,7 @@
namespace caffe2 {
CAFFE_DEFINE_KNOWN_TYPE(Tensor);
CAFFE_DEFINE_PREALLOCATED_KNOWN_TYPE(12, Tensor);
TensorPrinter::TensorPrinter(
const std::string& tensor_name,

View File

@ -435,6 +435,8 @@ class CAFFE2_API Tensor final {
}
};
CAFFE_DECLARE_PREALLOCATED_KNOWN_TYPE(12, Tensor)
using TensorCPU = Tensor;
constexpr int k_limit_default_ = 1000;

View File

@ -127,8 +127,7 @@ TEST(TypeMetaTest, CtorDtorAndCopy) {
// gtest seems to have some problem with function pointers and
// clang right now... Disabling it.
// TODO: figure out the real cause.
EXPECT_EQ(meta_b.copy(),
&(TypeMeta::_CopyNotAllowed<ClassNoAssignment>));
EXPECT_EQ(meta_b.copy(), &(detail::_CopyNotAllowed<ClassNoAssignment>));
#endif
}