-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
init tensor design doc #2579
init tensor design doc #2579
Conversation
doc/design/tensor_design.md
Outdated
`Allocation` is a [RAII](http://en.cppreference.com/w/cpp/language/raii) class template, which is used to handle a piece of memory. | ||
|
||
```cpp | ||
// Template parametr 'Device' can be 'CpuDevice' or 'GpuDevice' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parametr --> parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
doc/design/tensor_design.md
Outdated
|
||
// reshape tensor, data will be retained | ||
void reshape(const Dim<rank>& size); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mabe we should disable the use of = for tensor, and add a shared method to explicitly share a tensor
Tensor& operator=(const Tensor& src) = delete;
void ShareData(const Tensor& src);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
For Copy()
is already a global function, we'd better make ShareData()
global as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that ShareData method is substitute for operator=, so ShareData should be a class method too.
Following is the sample code:
Tensor<CpuDevice, double, 2> t_a(make_dim(2, 3), CpuDevice());
Tensor<CpuDevice, double, 2> tb;
t_b.ShareData(t_a);
doc/design/tensor_design.md
Outdated
|
||
```cpp | ||
// make a totally new tensor on CPU with raw constructor | ||
Tensor<CpuDevice, double, 2> t_a = Tensor<CpuDevice, double, 2>(make_dim(2,3), CpuDevice()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we disable the use of =, than this line should be:
Tensor<CpuDevice, double, 2> t_a(make_dim(2, 3), CpuDevice())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right. It also makes the code clearer.
And the helper function MakeTensor()
is not necessary anymore.
doc/design/tensor_design.md
Outdated
Allocation(void* ptr, size_t size, Device device); | ||
|
||
~Allocation(); | ||
//No copying! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe in the framework
directory, we could write DISABLE_COPY
macro, because there are many classes are be DISABLE_COPY
.
Otherwise, if we are happy to use boost
, there should be boost::non_copyable
base class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DISABLE_COPY
macro is a cool choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But macro cannot put in namespace
, DISABLE_COPY
will pollute the global namespace.
doc/design/tensor_design.md
Outdated
`Allocation` is a [RAII](http://en.cppreference.com/w/cpp/language/raii) class template, which is used to handle a piece of memory. | ||
|
||
```cpp | ||
// Template parametr 'Device' can be 'CpuDevice' or 'GpuDevice' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use GPUPlace
or GPUDevice
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also dwell on it. The key is how we name the template parameter, Place
or Device
?
doc/design/tensor_design.md
Outdated
Allocation& operator=(const Allocation&) = delete; | ||
|
||
void* ptr() const; | ||
void* end() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the end
is needed? ptr() + size() == end()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Member function end()
is simply for users' convenience.
With end()
, we can check the validity of a tensor's ptr_
like this:
CHECK(ptr_ >= alloc.ptr() && ptr_ <= alloc.end());
otherwise:
CHECK(ptr_ >= alloc.ptr() && ptr_ <= alloc.ptr() + alloc.size());
doc/design/tensor_design.md
Outdated
// make a new tensor by another existing tensor | ||
// new tensor and source tensor have the same numel but deferent rank | ||
template<int src_rank> | ||
Tensor(const Dim<rank>& size, const Tensor<Device, T, src_rank>& src); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the new tensor should be a writable view of src tensor?
If so, then new tensor can change data which shared with src tensor, i.e., new tensor can change src tensor. So, src tensor should not be marked as const
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your reminder. The two tensors Indeed share the same data block(which is called Allocation
in our design). I will remove the const
.
doc/design/tensor_design.md
Outdated
|
||
// return raw pointer to the data. | ||
T* raw_ptr() const; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a very critical method lack from Tensor
, maybe called SliceView
.
We cannot get a sub-tensor from a tensor right now, but sub-tensor is heavily used in RNN and Sparse Computation.
Tensor<float, CPU, 3> tensor;
Tensor b = tensor[1:]; // b is a matrix now.
And the memory between the original tensor and slice tensors should be shared. For example,
Tensor<float, CPU, 3>* originalTensor = new Tensor<float, CPU, 3>();
auto subTensor = (*originalTensor)[1:];
delete originalTensor;
subTensor.data(); // should be correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@QiJune and @Canpio tell me that Slice
is not a heavily used method. In RNN, we could also just use same allocation_, different ptr_
to share a Tensor.
This method is not an urgent need. It could be added later if it is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the current design, Allocation
can be shared by multiple Tensor
. Tensor
uses a shared_ptr to handle the Allocation
, and another void* to indicate the head of its own data. In other words, ptr_
will points to somewhere between Allocation::ptr()
and Allocation::end()
.
shared_ptr is responsible for Allocation
free when the last related Tensor
is destructed.
doc/design/tensor_design.md
Outdated
Dim<rank> stride() const; | ||
|
||
// return raw pointer to the 'idx'th element | ||
T* index(const Dim<D>& idx) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is D
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be rank
. fixed.
We can put this PR into project PaddlePaddle Refactoring for better tracking and management. |
The original discuess in * PaddlePaddle#2548 (comment) * PaddlePaddle#2579 (comment) This commit is just a proposal, let's do such kind of summarization in this PR.
doc/design/tensor_design.md
Outdated
Allocation(size_t size, Device device); | ||
|
||
// Creates a non-owned allocation | ||
Allocation(void *ptr, size_t size, Device device); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the use of this interface? Tensor can share the pointer of the same Allocation object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tensor
will never use this interface. However, as a general memory handler, Allocation
may be used by other concepts. Offering an interface to accept external memory may be helpful to increase its flexibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any other concept other than Tensor that will use Allocation
?
I think if we have not clearly used examples, we can remove the interface first. Such as the comment #2587 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I'm going to remove it.
doc/design/tensor_design.md
Outdated
size_t size() const; | ||
|
||
private: | ||
bool owned_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If remove the non-owned allocation, this can also be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed.
doc/design/tensor_design.md
Outdated
`Tensor` is the combination of Majel's `Buffer` and `Array`. | ||
|
||
```cpp | ||
template <typename Device, typename T, int rank> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rank -> Rank, like the Device
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
## Tensor | ||
|
||
`Tensor` is the combination of Majel's `Buffer` and `Array`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before Tensor's implementation, I think here need to explain why Device
, T
and rank
these three parameters to be placed in the template parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I will add this part later.
doc/design/tensor_design.md
Outdated
T *raw_ptr() const; | ||
|
||
// return tensor size | ||
Dim<rank> size() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be const Dim<rank>&
is better?
doc/design/tensor_design.md
Outdated
int numel() const; | ||
|
||
// return tensor stride | ||
Dim<rank> stride() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const Dim<rank>&
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
And also const Dim<Rank>& size() const;
doc/design/tensor_design.md
Outdated
Dim<rank> stride() const; | ||
|
||
// return raw pointer to the 'idx'th element | ||
T *index(const Dim<rank> &idx) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if the comment, did not understand the use of this interface. Can you explain how to use it?
doc/design/tensor_design.md
Outdated
} | ||
|
||
template <> | ||
struct Dim<1> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dim(int _head) : head(_head) {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
|
||
`size_` and `stride_` are `Dim` object. Inspired from Majel, `Dim` is a struct template for indicating tensor size and element index: | ||
|
||
```cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add the method of getting values from Dim
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I only show a few parts of Dim
.
You can check full definition of Dim
in https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/dim.h
|
||
`ptr_` points to the head of the memory piece and `size_` shows its length. `owned_` marks whether the memory piece is allocated by `allocation` itself, if so the memory will be freed when `allocation` is destructed. | ||
|
||
`Device` is something like Majel's `Place`. However, `Place` in Majel is an alias of `boost::variant`, while `Device` here is a certain class (can be specialized to `CpuDevice` or `GpuDevice`). `CpuDevice` and `GpuDevice` are exactly Majel's `CpuPlace` and `GpuPlace`, we rename them to fit the overall naming style. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we already havepaddle::platform::Place
, please consider using Place instead of introducing Device.
`Tensor` is the combination of Majel's `Buffer` and `Array`. | ||
|
||
```cpp | ||
template <typename T, int Rank, typename Device> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making Rank
a template parameter would prevent us from resizing a tensor easily.
Suppose that tensor A has dimensions <4,2,2>. It would be trivial to resize it to be <2,8> -- just make Tensor::Resize(DDim new_size)
to assign a new value to Tensor::size_
, if its type is DDim
other than Dim<3>
.
|
||
// check whether allocation_ is suitable for current size_ | ||
// if not, re-allocate then return ptr_ | ||
T* mutable_data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Deivce or Place shouldn't be a template parameter of Tensor; instead, they should be a parameter of Tensor::mutable_data
.
Also, T
must not be a template parameter of Tensor, but has to be a template parameter of Tensor::mutable_data
. This flexibility is a must-to-have for lazy memory allocation.
Here is the semantic of Tensor in my mind:
class Tensor {
public:
template <typename T /* must be POD types */
typename = std::enable_if< std::is_pod<T>::value >::type>
T* mutable_data(Place pl, DDim size) {
if (place_ != pl) {
paddle::memory::Free(place_, data_);
data_ = nullptr;
}
if (typeof(T)*size.product() < element_size_ * size_.product()) {
paddle::memory::Free(place_, data);
data_ = nullptr;
}
if (data == nullptr) {
element_size_ = sizeof(T);
place_ = pl;
size_ = size;
data_ = memory::Allocate(place_, element_size_ * size_.product());
}
return data_;
}
template <typename T
typename = std::enable_if< std::is_pod<T>::value >::type>
T* mutable_data(DDim size) {
return mutable_data<T>(paddle::framework::get_place(), size);
}
private:
Place place_; // record the place of data_.
size_t element_size_; // record element size of data_.
DDim size_; // record dimensionalities of data_.
void* data_; // Or use a Placeholder as in Variable to hide the type, so we can use unique_ptr here for data_.
};
see #2611 |
No description provided.