Skip to content
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

Closed
wants to merge 11 commits into from
Closed

Conversation

JiayiFeng
Copy link
Collaborator

No description provided.

`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'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parametr --> parameter

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


// reshape tensor, data will be retained
void reshape(const Dim<rank>& size);

Copy link
Member

@QiJune QiJune Jun 23, 2017

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);

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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.

Copy link
Member

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);


```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());
Copy link
Member

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())

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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.

Allocation(void* ptr, size_t size, Device device);

~Allocation();
//No copying!
Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator

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.

`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'
Copy link
Collaborator

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?

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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?

Allocation& operator=(const Allocation&) = delete;

void* ptr() const;
void* end() const;
Copy link
Collaborator

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()

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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());

// 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);
Copy link
Collaborator

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?

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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.


// return raw pointer to the data.
T* raw_ptr() const;

Copy link
Collaborator

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

Copy link
Collaborator

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.

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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.

Dim<rank> stride() const;

// return raw pointer to the 'idx'th element
T* index(const Dim<D>& idx) const;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is D?

Copy link
Collaborator Author

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.

@jacquesqiao
Copy link
Member

We can put this PR into project PaddlePaddle Refactoring for better tracking and management.

reyoung added a commit to reyoung/Paddle that referenced this pull request Jun 23, 2017
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.
Allocation(size_t size, Device device);

// Creates a non-owned allocation
Allocation(void *ptr, size_t size, Device device);
Copy link
Contributor

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.

Copy link
Collaborator Author

@JiayiFeng JiayiFeng Jun 23, 2017

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.

Copy link
Contributor

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).

Copy link
Collaborator Author

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.

@JiayiFeng JiayiFeng requested a review from a user June 24, 2017 10:23
size_t size() const;

private:
bool owned_;
Copy link
Contributor

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

`Tensor` is the combination of Majel's `Buffer` and `Array`.

```cpp
template <typename Device, typename T, int rank>
Copy link
Contributor

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.

Copy link
Collaborator Author

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`.

Copy link
Contributor

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.

Copy link
Collaborator Author

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.

T *raw_ptr() const;

// return tensor size
Dim<rank> size() const;
Copy link
Contributor

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?

int numel() const;

// return tensor stride
Dim<rank> stride() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const Dim<rank>&

Copy link
Collaborator Author

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;

Dim<rank> stride() const;

// return raw pointer to the 'idx'th element
T *index(const Dim<rank> &idx) const;
Copy link
Contributor

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?

}

template <>
struct Dim<1> {
Copy link
Contributor

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) {}

Copy link
Collaborator Author

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
Copy link
Contributor

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.

Copy link
Collaborator Author

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.
Copy link
Collaborator

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>
Copy link
Collaborator

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();
Copy link
Collaborator

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_.
};

@wangkuiyi wangkuiyi mentioned this pull request Jun 26, 2017
@JiayiFeng
Copy link
Collaborator Author

see #2611

@JiayiFeng JiayiFeng closed this Jun 28, 2017
@JiayiFeng JiayiFeng deleted the add_tensor_doc branch June 28, 2017 06:39
@reyoung reyoung moved this from Doing to Done in PaddlePaddle Refactoring: Phase 1 Aug 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

6 participants