diff --git a/paddle/framework/tensor.h b/paddle/framework/tensor.h index a0945e8055625ca4c21ea1c3fa9f27321ca9ba3c..62e0710a8244ce80e35d2cf9a922c4fd9f1a1b69 100644 --- a/paddle/framework/tensor.h +++ b/paddle/framework/tensor.h @@ -15,8 +15,8 @@ limitations under the License. */ #pragma once #include +#include #include -#include #include "paddle/framework/ddim.h" #include "paddle/framework/enforce.h" #include "paddle/memory/memory.h" @@ -27,45 +27,63 @@ namespace framework { class Tensor { public: - Tensor() : offset_(0) {} + Tensor() : numel_(0), offset_(0) {} - explicit Tensor(const DDim& dims) : dims_(dims), offset_(0) {} + Tensor& operator=(const Tensor& src) = delete; template const T* data() const { - PADDLE_ENFORCE( - holder_ != nullptr, - "Tenosr has not been initialized. Call Tensor::mutable_data first."); + CheckDims(); return reinterpret_cast( - reinterpret_cast(holder_->Ptr()) + offset_); + reinterpret_cast(holder_->ptr()) + offset_); } - template ::value>::type* = nullptr> + template T* mutable_data(DDim dims, paddle::platform::Place place) { - dims_ = dims; + set_dims(dims); + return mutable_data(place); + } + + template + T* mutable_data(paddle::platform::Place place) { + PADDLE_ENFORCE(numel_ > 0, + "Tensor::numel_ must be larger than zero to call " + "Tensor::mutable_data. Call Tensor::set_dim first."); if (holder_ == nullptr || - !(holder_->Place() == + !(holder_->place() == place) /* some versions of boost::variant don't have operator!= */ - || holder_->Size() < product(dims) * sizeof(T) + offset_) { - holder_.reset(new PlaceholderImpl(place, product(dims) * sizeof(T))); + || holder_->size() < numel_ * sizeof(T) + offset_) { + holder_.reset(new PlaceholderImpl(place, numel_ * sizeof(T))); offset_ = 0; } - return reinterpret_cast(reinterpret_cast(holder_->Ptr()) + + return reinterpret_cast(reinterpret_cast(holder_->ptr()) + offset_); } + template void ShareDataFrom(const Tensor& src) { - PADDLE_ENFORCE(src.holder_ != nullptr, - "Can not share data from an uninitialized tensor."); + src.CheckDims(); holder_ = src.holder_; - dims_ = src.dims_; + set_dims(src.dims()); offset_ = src.offset_; } + template + void CopyFrom(const Tensor& src, paddle::platform::Place dst_place) { + PADDLE_ENFORCE(platform::is_cpu_place(src.holder_->place()) && + platform::is_cpu_place(dst_place), + "Tensor::CopyFrom only support CPU now."); + src.CheckDims(); + size_t size = src.numel_ * sizeof(T); + set_dims(src.dims()); + const void* src_ptr = static_cast(src.data()); + void* dst_ptr = static_cast(mutable_data(dst_place)); + memcpy(dst_ptr, src_ptr, size); + } + + template Tensor Slice(const int& begin_idx, const int& end_idx) const { - PADDLE_ENFORCE(holder_ != nullptr, - "The sliced tenosr has not been initialized."); + CheckDims(); PADDLE_ENFORCE(begin_idx >= 0 && end_idx <= dims_[0], "Slice index is less than zero or out of bound."); PADDLE_ENFORCE(begin_idx < end_idx, @@ -78,12 +96,21 @@ class Tensor { } Tensor dst; dst.holder_ = holder_; - dst.dims_ = dims_; - dst.dims_[0] = end_idx - begin_idx; - dst.offset_ = offset_ + begin_idx * base * holder_->TypeSize(); + DDim dst_dims = dims_; + dst_dims[0] = end_idx - begin_idx; + dst.set_dims(dst_dims); + dst.offset_ = offset_ + begin_idx * base * sizeof(T); return dst; } + void set_dims(const DDim& dims) { + if (dims == dims_) { + return; + } + dims_ = dims; + numel_ = product(dims_); + } + DDim dims() const { return dims_; } private: @@ -91,10 +118,9 @@ class Tensor { // parameter of Variable. struct Placeholder { virtual ~Placeholder() {} - virtual void* Ptr() const = 0; - virtual paddle::platform::Place Place() const = 0; - virtual size_t Size() const = 0; - virtual size_t TypeSize() const = 0; + virtual void* ptr() const = 0; + virtual paddle::platform::Place place() const = 0; + virtual size_t size() const = 0; }; template @@ -118,18 +144,27 @@ class Tensor { place_(place), size_(size) {} - virtual void* Ptr() const { return static_cast(ptr_.get()); } - virtual size_t Size() const { return size_; } - virtual paddle::platform::Place Place() const { return place_; } - virtual size_t TypeSize() const { return sizeof(T); } + virtual void* ptr() const { return static_cast(ptr_.get()); } + virtual size_t size() const { return size_; } + virtual paddle::platform::Place place() const { return place_; } std::unique_ptr ptr_; paddle::platform::Place place_; // record the place of ptr_. size_t size_; // size of the memory block. }; + template + inline void CheckDims() const { + PADDLE_ENFORCE(holder_ != nullptr, + "Tenosr holds no memory. Call Tensor::mutable_data first."); + PADDLE_ENFORCE(holder_->size() >= numel_ * sizeof(T) + offset_, + "Tensor's dims_ is out of bound. Call Tensor::mutable_data " + "first to re-allocate memory."); + } + std::shared_ptr holder_; // holds the memory block if allocated. DDim dims_; + size_t numel_; // cache of `product(dims_)` size_t offset_; // marks the begin of tensor data area. }; diff --git a/paddle/framework/tensor_test.cc b/paddle/framework/tensor_test.cc index f4822838cfbd27656232a23b14f716f2fbe510e0..255f69372f4f06609471b9ff7a9b9ce790fcddf0 100644 --- a/paddle/framework/tensor_test.cc +++ b/paddle/framework/tensor_test.cc @@ -18,7 +18,8 @@ TEST(Tensor, Dims) { using namespace paddle::framework; using namespace paddle::platform; - Tensor tt(make_ddim({2, 3, 4})); + Tensor tt; + tt.set_dims(make_ddim({2, 3, 4})); DDim dims = tt.dims(); ASSERT_EQ(arity(dims), 3); for (int i = 0; i < 3; ++i) { @@ -35,7 +36,7 @@ TEST(Tensor, DataAssert) { } catch (paddle::framework::EnforceNotMet err) { caught = true; std::string msg = - "Tenosr has not been initialized. Call Tensor::mutable_data first."; + "Tenosr holds no memory. Call Tensor::mutable_data first."; const char* what = err.what(); for (size_t i = 0; i < msg.length(); ++i) { ASSERT_EQ(what[i], msg[i]); @@ -104,19 +105,18 @@ TEST(Tensor, ShareDataFrom) { // Try to share data form uninitialized tensor bool caught = false; try { - dst_tensor.ShareDataFrom(src_tensor); + dst_tensor.ShareDataFrom(src_tensor); } catch (EnforceNotMet err) { caught = true; - std::string msg = "Can not share data from an uninitialized tensor."; - const char* what = err.what(); - for (size_t i = 0; i < msg.length(); ++i) { - ASSERT_EQ(what[i], msg[i]); + std::string msg = "Tenosr holds no memory. Call Tensor::mutable_data +first."; const char* what = err.what(); for (size_t i = 0; i < msg.length(); +++i) { ASSERT_EQ(what[i], msg[i]); } } ASSERT_TRUE(caught); src_tensor.mutable_data(make_ddim({2, 3, 4}), CPUPlace()); - dst_tensor.ShareDataFrom(src_tensor); + dst_tensor.ShareDataFrom(src_tensor); ASSERT_EQ(src_tensor.data(), dst_tensor.data()); } @@ -124,7 +124,7 @@ TEST(Tensor, ShareDataFrom) { Tensor src_tensor; Tensor dst_tensor; src_tensor.mutable_data(make_ddim({2, 3, 4}), GPUPlace()); - dst_tensor.ShareDataFrom(src_tensor); + dst_tensor.ShareDataFrom(src_tensor); ASSERT_EQ(src_tensor.data(), dst_tensor.data()); } } @@ -135,7 +135,7 @@ TEST(Tensor, Slice) { { Tensor src_tensor; src_tensor.mutable_data(make_ddim({5, 3, 4}), CPUPlace()); - Tensor slice_tensor = src_tensor.Slice(1, 3); + Tensor slice_tensor = src_tensor.Slice(1, 3); DDim slice_dims = slice_tensor.dims(); ASSERT_EQ(arity(slice_dims), 3); EXPECT_EQ(slice_dims[0], 2); @@ -158,7 +158,7 @@ TEST(Tensor, Slice) { { Tensor src_tensor; src_tensor.mutable_data(make_ddim({6, 9}), GPUPlace()); - Tensor slice_tensor = src_tensor.Slice(2, 6); + Tensor slice_tensor = src_tensor.Slice(2, 6); DDim slice_dims = slice_tensor.dims(); ASSERT_EQ(arity(slice_dims), 2); EXPECT_EQ(slice_dims[0], 4); @@ -178,4 +178,29 @@ TEST(Tensor, Slice) { } } +TEST(Tensor, CopyFrom) { + using namespace paddle::framework; + using namespace paddle::platform; + + Tensor src_tensor; + int* src_ptr = src_tensor.mutable_data(make_ddim({3, 3}), CPUPlace()); + int arr[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + memcpy(src_ptr, arr, 9 * sizeof(int)); + Tensor dst_tensor; + dst_tensor.CopyFrom(src_tensor, CPUPlace()); + const int* dst_ptr = dst_tensor.data(); + ASSERT_NE(src_ptr, dst_ptr); + for (size_t i = 0; i < 9; ++i) { + EXPECT_EQ(src_ptr[i], dst_ptr[i]); + } + + Tensor slice_tensor = src_tensor.Slice(1, 2); + dst_tensor.CopyFrom(slice_tensor, CPUPlace()); + const int* slice_ptr = slice_tensor.data(); + dst_ptr = dst_tensor.data(); + ASSERT_NE(dst_ptr, slice_ptr); + for (size_t i = 0; i < 3; ++i) { + EXPECT_EQ(dst_ptr[i], slice_ptr[i]); + } +} */ \ No newline at end of file