mirror of
https://github.com/zebrajr/pytorch.git
synced 2025-12-07 12:21:27 +01:00
[Profiler] Use strong typedef for Tensor ID (#85718)
I want add Tensor ID to allocations (for allocs which are `StorageImpl`s). To keep things safe and organized I need to pull the ID type into a standalone entity, which makes it an ideal time to convert to a strong typedef. Differential Revision: [D39788872](https://our.internmc.facebook.com/intern/diff/D39788872/) Pull Request resolved: https://github.com/pytorch/pytorch/pull/85718 Approved by: https://github.com/chaekit
This commit is contained in:
parent
282d8dfa68
commit
f23f362c5d
|
|
@ -837,7 +837,7 @@ void calculate_unique_tensor_ids(std::vector<result_ptr_t>& sorted_results) {
|
|||
storage_id_t storage_id_;
|
||||
|
||||
// Used to assign the result.
|
||||
std::reference_wrapper<c10::optional<size_t>> id_ref_;
|
||||
std::reference_wrapper<c10::optional<TensorID>> id_ref_;
|
||||
};
|
||||
std::vector<TensorStoragePair> tensors;
|
||||
|
||||
|
|
@ -906,7 +906,7 @@ void calculate_unique_tensor_ids(std::vector<result_ptr_t>& sorted_results) {
|
|||
// Step 4) Write back to metadata
|
||||
// --------------------------------------------------------------------------
|
||||
for (const auto& t : tensors) {
|
||||
t.id_ref_.get() = id_map.at(t.storage_id_);
|
||||
t.id_ref_.get() = TensorID(id_map.at(t.storage_id_));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,25 @@ using StorageImplData = strong::type<
|
|||
strong::hashable,
|
||||
strong::boolean>;
|
||||
|
||||
// Identity is a complex concept in PyTorch. A Tensor might not have a
|
||||
// an associated storage, multiple Tensors might share the same underlying
|
||||
// storage, the storage of a Tensor might change over time, etc.
|
||||
//
|
||||
// For the purpose of profiling we're mostly interested in data flow
|
||||
// analysis. As a result, we can take an expansive view of identity:
|
||||
// Tensors share an ID if they share a TensorImpl or storage data.
|
||||
//
|
||||
// This identity equality is transitive; If Tensors T0 and T1 share a storage
|
||||
// S0 and T1 later points to a different storage S1 then all Tensors which
|
||||
// point to either S0 or S1 are considered to have the same identity. (Since
|
||||
// profiler cannot reason beyond that.)
|
||||
//
|
||||
// The profiler will handle lifetime analysis to ensure that identities do
|
||||
// not run afoul of the ABA problem. This does, however, mean that identities
|
||||
// can only be assigned when memory profiling is enabled. (And we cannot
|
||||
// handle ABA for TensorImpl as those allocations are not instrumented.)
|
||||
using TensorID = strong::type<size_t, struct TensorID_, strong::regular>;
|
||||
|
||||
struct RawTensorMetadata {
|
||||
TensorImplAddress impl_;
|
||||
StorageImplData data_;
|
||||
|
|
@ -75,24 +94,7 @@ struct TensorMetadata : public RawTensorMetadata {
|
|||
return {device_type_, device_index_};
|
||||
}
|
||||
|
||||
// Identity is a complex concept in PyTorch. A Tensor might not have a
|
||||
// an associated storage, multiple Tensors might share the same underlying
|
||||
// storage, the storage of a Tensor might change over time, etc.
|
||||
//
|
||||
// For the purpose of profiling we're mostly interested in data flow
|
||||
// analysis. As a result, we can take an expansive view of identity:
|
||||
// Tensors share an ID if they share a TensorImpl or storage data.
|
||||
//
|
||||
// This identity equality is transitive; If Tensors T0 and T1 share a storage
|
||||
// S0 and T1 later points to a different storage S1 then all Tensors which
|
||||
// point to either S0 or S1 are considered to have the same identity. (Since
|
||||
// profiler cannot reason beyond that.)
|
||||
//
|
||||
// The profiler will handle lifetime analysis to ensure that identities do
|
||||
// not run afoul of the ABA problem. This does, however, mean that identities
|
||||
// can only be assigned when memory profiling is enabled. (And we cannot
|
||||
// handle ABA for TensorImpl as those allocations are not instrumented.)
|
||||
c10::optional<size_t> id_;
|
||||
c10::optional<TensorID> id_;
|
||||
};
|
||||
|
||||
struct Inputs {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
namespace pybind11 {
|
||||
namespace detail {
|
||||
using torch::profiler::impl::StorageImplData;
|
||||
using torch::profiler::impl::TensorID;
|
||||
using torch::profiler::impl::TensorImplAddress;
|
||||
|
||||
template <>
|
||||
|
|
@ -17,6 +18,9 @@ struct type_caster<StorageImplData>
|
|||
template <>
|
||||
struct type_caster<TensorImplAddress>
|
||||
: public strong_pointer_type_caster<TensorImplAddress> {};
|
||||
|
||||
template <>
|
||||
struct type_caster<TensorID> : public strong_uint_type_caster<TensorID> {};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
namespace pybind11 {
|
||||
namespace detail {
|
||||
// Strong typedefs don't make much sense in Python since everything is duck
|
||||
// typed. So instead we simply cast them to ints, return them, and let the
|
||||
// caller handle correctness.
|
||||
// typed. So instead we simply extract the underlying value and let the caller
|
||||
// handle correctness.
|
||||
template <typename T>
|
||||
struct strong_pointer_type_caster {
|
||||
template <typename T_>
|
||||
|
|
@ -29,5 +29,22 @@ struct strong_pointer_type_caster {
|
|||
|
||||
PYBIND11_TYPE_CASTER(T, _("strong_pointer"));
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct strong_uint_type_caster {
|
||||
template <typename T_>
|
||||
static handle cast(
|
||||
T_&& src,
|
||||
return_value_policy /*policy*/,
|
||||
handle /*parent*/) {
|
||||
return handle(THPUtils_packUInt64(src.value_of()));
|
||||
}
|
||||
|
||||
bool load(handle /*src*/, bool /*convert*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PYBIND11_TYPE_CASTER(T, _("strong_uint"));
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user