1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #ifndef TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
17 #define TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
18
19 #include <cstdint>
20 #include <type_traits>
21
22 #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
23 #include "tensorflow/core/framework/allocator.h"
24 #include "tensorflow/core/framework/tensor_shape.h"
25 #include "tensorflow/core/framework/tensor_types.h"
26 #include "tensorflow/core/framework/types.h"
27 #include "tensorflow/core/framework/types.pb.h"
28 #include "tensorflow/core/lib/core/refcount.h"
29 #include "tensorflow/core/lib/core/status.h"
30 #include "tensorflow/core/lib/core/stringpiece.h"
31 #include "tensorflow/core/lib/gtl/inlined_vector.h"
32 #include "tensorflow/core/platform/logging.h"
33 #include "tensorflow/core/platform/macros.h"
34 #include "tensorflow/core/platform/mem.h"
35 #include "tensorflow/core/platform/types.h"
36
37 namespace tensorflow {
38
39 // Forward declarations. In particular, we forward declare protos so that their
40 // symbols can be removed from .so exports.
41 class AllocationDescription;
42 class Allocator;
43 class OpKernelContext;
44 class Tensor;
45 class TensorBuffer;
46 class TensorCApi;
47 class TensorInterface;
48 class TensorCord;
49 class TensorDescription;
50 class TensorProto;
51 class Var;
52
53 namespace batch_util {
54 Status CopyElementToSlice(Tensor element, Tensor* parent, int64_t index);
55 Status CopySliceToElement(const Tensor& parent, Tensor* element, int64_t index);
56 Status MaybeMoveSliceToElement(Tensor* parent, Tensor* element, int64_t index);
57 Status CopyContiguousSlices(const Tensor& src, int64_t src_offset,
58 int64_t dst_offset, int64_t num_slices,
59 Tensor* dst);
60 } // namespace batch_util
61
62 /// @ingroup core
63
64 /// Interface to access the raw ref-counted data buffer.
65 class TensorBuffer : public core::RefCounted {
66 public:
TensorBuffer(void * data_ptr)67 explicit TensorBuffer(void* data_ptr) : data_(data_ptr) {}
~TensorBuffer()68 ~TensorBuffer() override {}
69
70 /// \brief data() points to a memory region of size() bytes.
71 ///
72 /// NOTE(mrry): The `data()` method is not virtual for performance reasons.
73 /// It can be called multiple times when the contents of a `Tensor` are
74 /// accessed, and so making it non-virtual allows the body to be inlined.
data()75 void* data() const { return data_; }
76
77 /// \brief Size (in bytes) of the buffer.
78 virtual size_t size() const = 0;
79
80 /// \brief If this TensorBuffer is sub-buffer of another TensorBuffer,
81 /// returns that TensorBuffer. Otherwise, returns this.
82 virtual TensorBuffer* root_buffer() = 0;
83
84 /// \brief Fills metadata about the allocation into the proto.
85 virtual void FillAllocationDescription(
86 AllocationDescription* proto) const = 0;
87
88 virtual bool GetAllocatedBytes(size_t* out_bytes) const;
89
90 /// \brief Helper method to reinterpret the buffer as an array of `T`.
91 template <typename T>
base()92 T* base() const {
93 return reinterpret_cast<T*>(data());
94 }
95
96 /// \brief Whether this TensorBuffer owns the underlying memory.
OwnsMemory()97 virtual bool OwnsMemory() const { return true; }
98
99 /// \brief The type of the underlying memory.
GetMemoryType()100 virtual AllocatorMemoryType GetMemoryType() const {
101 return AllocatorMemoryType::kUnknown;
102 }
103
104 private:
105 void* const data_;
106 };
107
108 /// Represents an n-dimensional array of values.
109 class Tensor {
110 public:
111 /// \brief Creates a 1-dimensional, 0-element float tensor.
112 ///
113 /// The returned Tensor is not a scalar (shape {}), but is instead
114 /// an empty one-dimensional Tensor (shape {0}, NumElements() ==
115 /// 0). Since it has no elements, it does not need to be assigned a
116 /// value and is initialized by default (IsInitialized() is
117 /// true). If this is undesirable, consider creating a one-element
118 /// scalar which does require initialization:
119 ///
120 /// ```c++
121 ///
122 /// Tensor(DT_FLOAT, TensorShape({}))
123 ///
124 /// ```
125 Tensor();
126
127 /// \brief Creates a Tensor of the given `type` and `shape`. If
128 /// LogMemory::IsEnabled() the allocation is logged as coming from
129 /// an unknown kernel and step. Calling the Tensor constructor
130 /// directly from within an Op is deprecated: use the
131 /// OpKernelConstruction/OpKernelContext allocate_* methods to
132 /// allocate a new tensor, which record the kernel and step.
133 ///
134 /// The underlying buffer is allocated using a `CPUAllocator`.
135 Tensor(DataType type, const TensorShape& shape);
136
137 /// \brief Creates a tensor with the input `type` and `shape`, using
138 /// the allocator `a` to allocate the underlying buffer. If
139 /// LogMemory::IsEnabled() the allocation is logged as coming from
140 /// an unknown kernel and step. Calling the Tensor constructor
141 /// directly from within an Op is deprecated: use the
142 /// OpKernelConstruction/OpKernelContext allocate_* methods to
143 /// allocate a new tensor, which record the kernel and step.
144 ///
145 /// `a` must outlive the lifetime of this Tensor.
146 Tensor(Allocator* a, DataType type, const TensorShape& shape);
147
148 /// \brief Creates a tensor with the input `type` and `shape`, using
149 /// the allocator `a` and the specified "allocation_attr" to
150 /// allocate the underlying buffer. If the kernel and step are known
151 /// allocation_attr.allocation_will_be_logged should be set to true
152 /// and LogMemory::RecordTensorAllocation should be called after the
153 /// tensor is constructed. Calling the Tensor constructor directly
154 /// from within an Op is deprecated: use the
155 /// OpKernelConstruction/OpKernelContext allocate_* methods to
156 /// allocate a new tensor, which record the kernel and step.
157 ///
158 /// `a` must outlive the lifetime of this Tensor.
159 Tensor(Allocator* a, DataType type, const TensorShape& shape,
160 const AllocationAttributes& allocation_attr);
161
162 /// \brief Creates a tensor with the input datatype, shape and buf.
163 ///
164 /// Acquires a ref on buf that belongs to this Tensor.
165 Tensor(DataType type, const TensorShape& shape, TensorBuffer* buf);
166
167 /// \brief Creates a tensor with the input datatype, shape and buf.
168 ///
169 /// Takes an ownership of the bufffer from the reference counted pointer.
170 Tensor(DataType type, TensorShape shape, core::RefCountPtr<TensorBuffer> buf);
171
172 /// \brief Creates an empty Tensor of the given data type.
173 ///
174 /// Like Tensor(), returns a 1-dimensional, 0-element Tensor with
175 /// IsInitialized() returning True. See the Tensor() documentation
176 /// for details.
177 explicit Tensor(DataType type);
178
179 /// \brief Initializes a tensor with the input `type` and `shape`, or returns
180 /// an error and leaves `out_tensor` unmodified. This factory method should be
181 /// used instead of the corresponding constructor if calling code cannot
182 /// validate that the `DataType` is valid and supported.
183 ///
184 /// The underlying buffer is allocated using a `CPUAllocator`.
185 static Status BuildTensor(DataType type, const TensorShape& shape,
186 Tensor* out_tensor);
187
188 private:
189 // A tag type for selecting the `Tensor` constructor overload that creates a
190 // scalar tensor in host memory.
191 struct host_scalar_tag {};
192
193 class HostScalarTensorBufferBase;
194 template <typename T>
195 struct ValueAndTensorBuffer;
196
197 // Creates a tensor with the given scalar `value` in CPU memory.
198 template <typename T>
199 Tensor(T value, host_scalar_tag tag);
200
201 public:
202 // A series of specialized constructors for scalar tensors in host memory.
203 //
204 // NOTE: The `Variant` host-scalar constructor is not defined, because Variant
205 // is implicitly constructible from many different types, and this causes
206 // ambiguities with some compilers.
Tensor(float scalar_value)207 explicit Tensor(float scalar_value)
208 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(double scalar_value)209 explicit Tensor(double scalar_value)
210 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int32_t scalar_value)211 explicit Tensor(int32_t scalar_value)
212 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint32 scalar_value)213 explicit Tensor(uint32 scalar_value)
214 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint16 scalar_value)215 explicit Tensor(uint16 scalar_value)
216 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint8 scalar_value)217 explicit Tensor(uint8 scalar_value)
218 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int16_t scalar_value)219 explicit Tensor(int16_t scalar_value)
220 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int8_t scalar_value)221 explicit Tensor(int8_t scalar_value)
222 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(tstring scalar_value)223 explicit Tensor(tstring scalar_value)
224 : Tensor(std::move(scalar_value), host_scalar_tag{}) {}
Tensor(complex64 scalar_value)225 explicit Tensor(complex64 scalar_value)
226 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(complex128 scalar_value)227 explicit Tensor(complex128 scalar_value)
228 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(int64_t scalar_value)229 explicit Tensor(int64_t scalar_value)
230 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(uint64 scalar_value)231 explicit Tensor(uint64 scalar_value)
232 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(bool scalar_value)233 explicit Tensor(bool scalar_value)
234 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint8 scalar_value)235 explicit Tensor(qint8 scalar_value)
236 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(quint8 scalar_value)237 explicit Tensor(quint8 scalar_value)
238 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint16 scalar_value)239 explicit Tensor(qint16 scalar_value)
240 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(quint16 scalar_value)241 explicit Tensor(quint16 scalar_value)
242 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(qint32 scalar_value)243 explicit Tensor(qint32 scalar_value)
244 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(bfloat16 scalar_value)245 explicit Tensor(bfloat16 scalar_value)
246 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(Eigen::half scalar_value)247 explicit Tensor(Eigen::half scalar_value)
248 : Tensor(scalar_value, host_scalar_tag{}) {}
Tensor(ResourceHandle scalar_value)249 explicit Tensor(ResourceHandle scalar_value)
250 : Tensor(std::move(scalar_value), host_scalar_tag{}) {}
251
252 // NOTE: The `const char*` host-scalar constructor is provided as a
253 // convenience because otherwise passing a string literal would surprisingly
254 // construct a DT_BOOL tensor.
Tensor(const char * scalar_value)255 explicit Tensor(const char* scalar_value)
256 : Tensor(tstring(scalar_value), host_scalar_tag{}) {}
257
258 /// Copy constructor.
259 Tensor(const Tensor& other);
260
261 /// \brief Move constructor. After this call, <other> is safely destructible
262 /// can be assigned to, and IsInitialized() can be called and will return
263 /// false. Other calls on <other> (e.g. shape manipulation) are not valid.
264 Tensor(Tensor&& other);
265
266 // Explicitly delete constructor that take a pointer (except char*)
267 // so that the pointer doesn't get implicitly cast to bool.
268 template <typename T, typename std::enable_if<!std::is_same<T, char>::value,
269 T>::type* = nullptr>
270 explicit Tensor(T* t) = delete;
271
272 ~Tensor();
273
274 /// Returns the data type.
dtype()275 DataType dtype() const { return shape_.data_type(); }
276
277 /// Returns the shape of the tensor.
shape()278 const TensorShape& shape() const { return shape_; }
279
280 /// \brief Convenience accessor for the tensor shape.
281 ///
282 /// For all shape accessors, see comments for relevant methods of
283 /// `TensorShape` in `tensor_shape.h`.
dims()284 int dims() const { return shape().dims(); }
285
286 /// Convenience accessor for the tensor shape.
dim_size(int d)287 int64_t dim_size(int d) const { return shape().dim_size(d); }
288
289 /// Convenience accessor for the tensor shape.
NumElements()290 int64_t NumElements() const { return shape().num_elements(); }
291
IsSameSize(const Tensor & b)292 bool IsSameSize(const Tensor& b) const {
293 return shape().IsSameSize(b.shape());
294 }
295
296 // True iff the two tensors use the same underlying refcounted storage
297 bool SharesBufferWith(const Tensor& b) const;
298
299 /// \brief If necessary, has this Tensor been initialized?
300 ///
301 /// Zero-element Tensors are always considered initialized, even if they
302 /// have never been assigned to and do not have any memory allocated.
303 bool IsInitialized() const;
304
305 /// Returns the estimated memory usage of this tensor.
306 size_t TotalBytes() const;
307
308 // Returns the size of allocated memory for this tensor.
309 size_t AllocatedBytes() const;
310
311 /// Returns true iff this tensor is aligned.
IsAligned()312 bool IsAligned() const {
313 #if EIGEN_MAX_ALIGN_BYTES == 0
314 return true;
315 #else
316 void* ptr = base<void>();
317 return dtype() == DT_STRING || NumElements() == 0 ||
318 (reinterpret_cast<intptr_t>(ptr) % EIGEN_MAX_ALIGN_BYTES == 0);
319 #endif
320 }
321
322 /// Assign operator. This tensor shares other's underlying storage.
323 Tensor& operator=(const Tensor& other) {
324 CopyFromInternal(other, other.shape());
325 return *this;
326 }
327
328 /// Move operator. See move constructor for details.
329 Tensor& operator=(Tensor&& other);
330
331 /// \brief Copy the other tensor into this tensor and reshape it.
332 ///
333 /// This tensor shares other's underlying storage. Returns `true`
334 /// iff `other.shape()` has the same number of elements of the given
335 /// `shape`.
CopyFrom(const Tensor & other,const TensorShape & shape)336 bool CopyFrom(const Tensor& other,
337 const TensorShape& shape) TF_MUST_USE_RESULT {
338 if (other.NumElements() != shape.num_elements()) return false;
339 CopyFromInternal(other, shape);
340 return true;
341 }
342
343 /// \brief Slice this tensor along the 1st dimension.
344
345 /// I.e., the returned tensor satisfies
346 /// returned[i, ...] == this[dim0_start + i, ...].
347 /// The returned tensor shares the underlying tensor buffer with this
348 /// tensor.
349 ///
350 /// NOTE: The returned tensor may not satisfy the same alignment
351 /// requirement as this tensor depending on the shape. The caller
352 /// must check the returned tensor's alignment before calling certain
353 /// methods that have alignment requirement (e.g., `flat()`, `tensor()`).
354 ///
355 /// NOTE: When fed with an N-dimensional tensor, this method returns a tensor
356 /// also with N dimensions. If you want to select a sub tensor, see SubSlice.
357 ///
358 /// REQUIRES: `dims()` >= 1
359 /// REQUIRES: `0 <= dim0_start <= dim0_limit <= dim_size(0)`
360 Tensor Slice(int64_t dim0_start, int64_t dim0_limit) const;
361
362 /// \brief Select a subslice from this tensor along the 1st dimension.
363 ///
364 /// When fed with an N-dimensional tensor, this method returns a tensor with
365 /// N-1 dimensions, where the returned tensor is a subslice of the input
366 /// tensor along the first dimension. The N-1 dimensions of the returned
367 /// tensor are the last N-1 dimensions of the input tensor.
368 ///
369 /// NOTE: The returned tensor may not satisfy the same alignment
370 /// requirement as this tensor depending on the shape. The caller
371 /// must check the returned tensor's alignment before calling certain
372 /// methods that have alignment requirement (e.g., `flat()`, `tensor()`).
373 ///
374 /// REQUIRES: `dims()` >= 1
375 /// REQUIRES: `0 <= index < dim_size(0)`
376 Tensor SubSlice(int64_t index) const;
377
378 /// \brief Parse `other` and construct the tensor.
379
380 /// Returns `true` iff the parsing succeeds. If the parsing fails,
381 /// the state of `*this` is unchanged.
382 bool FromProto(const TensorProto& other) TF_MUST_USE_RESULT;
383 bool FromProto(Allocator* a, const TensorProto& other) TF_MUST_USE_RESULT;
384
385 /// \brief Fills in `proto` with `*this` tensor's content.
386 ///
387 /// `AsProtoField()` fills in the repeated field for `proto.dtype()`, while
388 /// `AsProtoTensorContent()` encodes the content in `proto.tensor_content()`
389 /// in a compact form.
390 void AsProtoField(TensorProto* proto) const;
391 void AsProtoTensorContent(TensorProto* proto) const;
392
393 /// \brief Return the tensor data as an `Eigen::Tensor` with the type and
394 /// sizes of this `Tensor`.
395 ///
396 /// Use these methods when you know the data type and the number of
397 /// dimensions of the Tensor and you want an `Eigen::Tensor`
398 /// automatically sized to the `Tensor` sizes. The implementation check
399 /// fails if either type or sizes mismatch.
400 ///
401 /// Example:
402 ///
403 /// ```c++
404 ///
405 /// typedef float T;
406 /// Tensor my_mat(...built with Shape{rows: 3, cols: 5}...);
407 /// auto mat = my_mat.matrix<T>(); // 2D Eigen::Tensor, 3 x 5.
408 /// auto mat = my_mat.tensor<T, 2>(); // 2D Eigen::Tensor, 3 x 5.
409 /// auto vec = my_mat.vec<T>(); // CHECK fails as my_mat is 2D.
410 /// auto vec = my_mat.tensor<T, 3>(); // CHECK fails as my_mat is 2D.
411 /// auto mat = my_mat.matrix<int32>();// CHECK fails as type mismatch.
412 ///
413 /// ```
414 template <typename T>
vec()415 typename TTypes<T>::Vec vec() {
416 return tensor<T, 1>();
417 }
418
419 template <typename T>
matrix()420 typename TTypes<T>::Matrix matrix() {
421 return tensor<T, 2>();
422 }
423
424 template <typename T, size_t NDIMS>
425 typename TTypes<T, NDIMS>::Tensor tensor() TF_ATTRIBUTE_NOINLINE;
426
427 /// \brief Return the tensor data to an `Eigen::Tensor` with the
428 /// same size but a bitwise cast to the specified dtype `T`.
429 ///
430 /// Using a bitcast is useful for move and copy operations.
431 /// NOTE: this is the same as `tensor()` except a bitcast is allowed.
432 template <typename T, size_t NDIMS>
433 typename TTypes<T, NDIMS>::Tensor bit_casted_tensor();
434
435 /// \brief Return the tensor data to an `Eigen::Tensor` with the
436 /// last dimension elements converted into single elements of a larger type.
437 ///
438 /// For example, this is useful for kernels that can treat NCHW_VECT_C int8
439 /// tensors as NCHW int32 tensors. The sizeof(T) should equal the size of
440 /// the original element type * num elements in the original last dimension.
441 /// NDIMS should be 1 less than the original number of dimensions.
442 template <typename T, size_t NDIMS>
443 typename TTypes<T, NDIMS>::Tensor reinterpret_last_dimension();
444
445 /// \brief Return the tensor data as an `Eigen::Tensor` of the data type and a
446 /// specified shape.
447 ///
448 /// These methods allow you to access the data with the dimensions
449 /// and sizes of your choice. You do not need to know the number of
450 /// dimensions of the Tensor to call them. However, they `CHECK` that
451 /// the type matches and the dimensions requested creates an
452 /// `Eigen::Tensor` with the same number of elements as the tensor.
453 ///
454 /// Example:
455 ///
456 /// ```c++
457 ///
458 /// typedef float T;
459 /// Tensor my_ten(...built with Shape{planes: 4, rows: 3, cols: 5}...);
460 /// // 1D Eigen::Tensor, size 60:
461 /// auto flat = my_ten.flat<T>();
462 /// // 2D Eigen::Tensor 12 x 5:
463 /// auto inner = my_ten.flat_inner_dims<T>();
464 /// // 2D Eigen::Tensor 4 x 15:
465 /// auto outer = my_ten.shaped<T, 2>({4, 15});
466 /// // CHECK fails, bad num elements:
467 /// auto outer = my_ten.shaped<T, 2>({4, 8});
468 /// // 3D Eigen::Tensor 6 x 5 x 2:
469 /// auto weird = my_ten.shaped<T, 3>({6, 5, 2});
470 /// // CHECK fails, type mismatch:
471 /// auto bad = my_ten.flat<int32>();
472 ///
473 /// ```
474 template <typename T>
475 typename TTypes<T>::Flat flat();
476
477 template <typename T>
unaligned_flat()478 typename TTypes<T>::UnalignedFlat unaligned_flat() {
479 return unaligned_shaped<T, 1>({NumElements()});
480 }
481
482 /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing all
483 /// Tensor dimensions but the last NDIMS-1 into the first dimension of the
484 /// result. If NDIMS > dims() then leading dimensions of size 1 will be
485 /// added to make the output rank NDIMS.
486 template <typename T, size_t NDIMS = 2>
487 typename TTypes<T, NDIMS>::Tensor flat_inner_dims();
488
489 /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing all
490 /// Tensor dimensions but the first NDIMS-1 into the last dimension of the
491 /// result. If NDIMS > dims() then trailing dimensions of size 1 will be
492 /// added to make the output rank NDIMS.
493 template <typename T, size_t NDIMS = 2>
494 typename TTypes<T, NDIMS>::Tensor flat_outer_dims();
495
496 /// Returns the data as an Eigen::Tensor with NDIMS dimensions, collapsing the
497 /// first 'begin' Tensor dimensions into the first dimension of the result and
498 /// the Tensor dimensions of the last dims() - 'begin' - NDIMS into the last
499 /// dimension of the result. If 'begin' < 0 then the |'begin'| leading
500 /// dimensions of size 1 will be added. If 'begin' + NDIMS > dims() then
501 /// 'begin' + NDIMS - dims() trailing dimensions of size 1 will be added.
502 template <typename T, size_t NDIMS = 3>
503 typename TTypes<T, NDIMS>::Tensor flat_inner_outer_dims(int64_t begin);
504
505 template <typename T, size_t NDIMS>
506 typename TTypes<T, NDIMS>::Tensor shaped(gtl::ArraySlice<int64_t> new_sizes);
507
508 /// \brief Return the tensor data to an `Eigen::Tensor` with the new
509 /// shape specified in `new_sizes` and cast to a new dtype `T`.
510 ///
511 /// Using a bitcast is useful for move and copy operations.
512 /// The allowed bitcast is the only difference from `shaped()`.
513 template <typename T, size_t NDIMS>
514 typename TTypes<T, NDIMS>::Tensor bit_casted_shaped(
515 gtl::ArraySlice<int64_t> new_sizes);
516
517 template <typename T, size_t NDIMS>
518 typename TTypes<T, NDIMS>::UnalignedTensor unaligned_shaped(
519 gtl::ArraySlice<int64_t> new_sizes);
520
521 /// \brief Return the Tensor data as a `TensorMap` of fixed size 1:
522 /// `TensorMap<TensorFixedSize<T, 1>>`.
523
524 /// Using `scalar()` allows the compiler to perform optimizations as
525 /// the size of the tensor is known at compile time.
526 template <typename T>
527 typename TTypes<T>::Scalar scalar();
528
529 /// Const versions of all the methods above.
530 template <typename T>
vec()531 typename TTypes<T>::ConstVec vec() const {
532 return tensor<T, 1>();
533 }
534
535 template <typename T>
matrix()536 typename TTypes<T>::ConstMatrix matrix() const {
537 return tensor<T, 2>();
538 }
539
540 template <typename T, size_t NDIMS>
541 typename TTypes<T, NDIMS>::ConstTensor tensor() const TF_ATTRIBUTE_NOINLINE;
542
543 /// \brief Return the tensor data to an `Eigen::Tensor` with the
544 /// same size but a bitwise cast to the specified dtype `T`.
545 ///
546 /// Using a bitcast is useful for move and copy operations.
547 /// NOTE: this is the same as `tensor()` except a bitcast is allowed.
548 template <typename T, size_t NDIMS>
549 typename TTypes<T, NDIMS>::ConstTensor bit_casted_tensor() const;
550
551 /// \brief Return the tensor data to an `Eigen::Tensor` with the
552 /// last dimension elements converted into single elements of a larger type.
553 ///
554 /// For example, this is useful for kernels that can treat NCHW_VECT_C int8
555 /// tensors as NCHW int32 tensors. The sizeof(T) should equal the size of
556 /// the original element type * num elements in the original last dimension.
557 /// NDIMS should be 1 less than the original number of dimensions.
558 template <typename T, size_t NDIMS>
559 typename TTypes<T, NDIMS>::ConstTensor reinterpret_last_dimension() const;
560
561 template <typename T>
562 typename TTypes<T>::ConstFlat flat() const;
563
564 template <typename T>
unaligned_flat()565 typename TTypes<T>::UnalignedConstFlat unaligned_flat() const {
566 return unaligned_shaped<T, 1>({NumElements()});
567 }
568
569 template <typename T, size_t NDIMS>
570 typename TTypes<T, NDIMS>::ConstTensor shaped(
571 gtl::ArraySlice<int64_t> new_sizes) const;
572
573 /// \brief Return the tensor data to an `Eigen::Tensor` with the new
574 /// shape specified in `new_sizes` and cast to a new dtype `T`.
575 ///
576 /// Using a bitcast is useful for move and copy operations.
577 /// The allowed bitcast is the only difference from `shaped()`.
578 template <typename T, size_t NDIMS>
579 typename TTypes<T, NDIMS>::ConstTensor bit_casted_shaped(
580 gtl::ArraySlice<int64_t> new_sizes) const;
581
582 template <typename T, size_t NDIMS>
583 typename TTypes<T, NDIMS>::UnalignedConstTensor unaligned_shaped(
584 gtl::ArraySlice<int64_t> new_sizes) const;
585
586 template <typename T>
587 typename TTypes<T>::ConstScalar scalar() const;
588
589 template <typename T, size_t NDIMS = 2>
590 typename TTypes<T, NDIMS>::ConstTensor flat_inner_dims() const;
591
592 template <typename T, size_t NDIMS = 2>
593 typename TTypes<T, NDIMS>::ConstTensor flat_outer_dims() const;
594
595 template <typename T, size_t NDIMS = 3>
596 typename TTypes<T, NDIMS>::ConstTensor flat_inner_outer_dims(
597 int64_t begin) const;
598
599 /// Render the first `max_entries` values in `*this` into a string.
600 std::string SummarizeValue(int64_t max_entries, bool print_v2 = false) const;
601
602 /// A human-readable summary of the tensor suitable for debugging.
603 // `num_values` is the number of actual data values in the tensor
604 // included in the message. If the tensor might be resident in
605 // GPU/TPU memory use DeviceSafeDebugString instead.
606 std::string DebugString(int num_values) const;
DebugString()607 std::string DebugString() const { return DebugString(3); }
608
609 // Variant of DebugString() that should be used for possibly non-CPU tensors.
610 // If the tensor is not resident on CPU, we can't read its values as
611 // DebugString() does.
612 std::string DeviceSafeDebugString() const;
613
614 /// Fill in the `TensorDescription` proto with metadata about the
615 /// tensor that is useful for monitoring and debugging.
616 void FillDescription(TensorDescription* description) const;
617
618 /// \brief Returns a `StringPiece` mapping the current tensor's buffer.
619 ///
620 /// The returned `StringPiece` may point to memory location on devices
621 /// that the CPU cannot address directly.
622 ///
623 /// NOTE: The underlying tensor buffer is refcounted, so the lifetime
624 /// of the contents mapped by the `StringPiece` matches the lifetime of
625 /// the buffer; callers should arrange to make sure the buffer does
626 /// not get destroyed while the `StringPiece` is still used.
627 ///
628 /// REQUIRES: `DataTypeCanUseMemcpy(dtype())`.
629 StringPiece tensor_data() const;
630 void* data() const;
631
632 /// Copy the other tensor into this tensor, reshape it and reinterpret the
633 /// buffer's datatype. If an ok Status is returned, the two tensors now share
634 /// the same underlying storage.
635 ///
636 /// This call requires that the `other` tensor and the given type and shape
637 /// are "compatible" (i.e. they occupy the same number of bytes).
638 ///
639 /// Specifically:
640 ///
641 /// shape.num_elements() * DataTypeSize(type)
642 ///
643 /// must equal
644 ///
645 /// other.num_elements() * DataTypeSize(other.dtype())
646 ///
647 /// In addition, this function requires:
648 /// * DataTypeSize(other.dtype()) != 0
649 /// * DataTypeSize(type) != 0
650 ///
651 /// If any of the requirements are not met, errors::InvalidArgument is
652 /// returned.
653 Status BitcastFrom(const Tensor& other, DataType dtype,
654 const TensorShape& shape);
655
656 /// Like BitcastFrom, but CHECK fails if any preconditions are not met.
657 ///
658 /// Deprecated. Use BitcastFrom instead and check the returned Status.
UnsafeCopyFromInternal(const Tensor & other,DataType dtype,const TensorShape & shape)659 void UnsafeCopyFromInternal(const Tensor& other, DataType dtype,
660 const TensorShape& shape) {
661 TF_CHECK_OK(BitcastFrom(other, dtype, shape));
662 }
663
664 // Returns true if the refcount on buf_ and any possible underlying root
665 // buffer is one.
666 bool RefCountIsOne() const;
667
668 // Returns the type of the underlying memory.
GetMemoryType()669 AllocatorMemoryType GetMemoryType() const { return buf_->GetMemoryType(); }
670
671 private:
672 void CheckType(DataType expected_dtype) const;
673 void CheckTypeAndIsAligned(DataType expected_dtype) const;
674 void CheckIsAlignedAndSingleElement() const;
set_dtype(DataType t)675 void set_dtype(DataType t) { shape_.set_data_type(t); }
676
677 // TensorShape's InlineVector.
678 static gtl::InlinedVector<int64_t, 4> ComputeFlatInnerDims(
679 gtl::ArraySlice<int64_t> orig, int64_t num_out_dims);
680 static gtl::InlinedVector<int64_t, 4> ComputeFlatOuterDims(
681 gtl::ArraySlice<int64_t> orig, int64_t num_out_dims);
682
683 TensorShape shape_;
684 TensorBuffer* buf_;
685
686 friend class DMAHelper; // For access to buf_.
687 friend class TensorCApi; // For access to buf_.
688 friend class TensorCord; // For access to buf_.
689 friend class TensorReference; // For access to buf_.
690 friend class VariableOp; // For access to set_shape.
691 friend class AutoReloadVariableOp; // For access to set_shape.
692 friend class TensorTestHelper; // For access to set_shape.
693 friend class TensorInterface; // For access to set_shape.
694 friend class CastOpBase; // For access to set_dtype.
695 friend class ScopedAllocator; // For access to buf_.
696 friend Status batch_util::CopyElementToSlice(
697 Tensor element, Tensor* parent,
698 int64_t index); // For access to base<T>().
699 friend Status batch_util::CopySliceToElement(
700 const Tensor& parent, Tensor* element,
701 int64_t index); // For access to base<T>().
702 friend Status batch_util::MaybeMoveSliceToElement(
703 Tensor* parent, Tensor* element,
704 int64_t index); // For access to base<T>().
705 friend Status batch_util::CopyContiguousSlices(
706 const Tensor& src, int64_t src_offset, int64_t dst_offset,
707 int64_t num_slices,
708 Tensor* dst); // For access to base<T>().
709
710 bool CanUseDMA() const;
711
712 // Only needed by variable op to set the shape of an uninitialized
713 // Tensor.
714 // TODO: Remove this when we have a better story for detecting
715 // uninitialized tensors.
set_shape(const TensorShape & shape)716 void set_shape(const TensorShape& shape) {
717 DataType dt = dtype();
718 shape_ = shape;
719 set_dtype(dt);
720 }
721
CopyFromInternal(const Tensor & other,const TensorShape & shape)722 inline void CopyFromInternal(const Tensor& other, const TensorShape& shape) {
723 DCHECK_EQ(shape.num_elements(), other.NumElements());
724 // Data type will be overwritten if this == &other, since dtype is part of
725 // shape.
726 DataType other_dtype = other.dtype();
727 shape_ = shape;
728 set_dtype(other_dtype);
729 if (buf_ != other.buf_) {
730 if (buf_) buf_->Unref();
731 buf_ = other.buf_;
732 if (buf_) buf_->Ref();
733 }
734 }
735
736 template <typename T>
737 T* base() const;
738
739 template <size_t NDIMS>
740 void FillDimsAndValidateCompatibleShape(
741 gtl::ArraySlice<int64_t> new_sizes,
742 Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const;
743
744 template <typename T, size_t NDIMS>
745 void FillDimsAndValidateCompatibleShape(
746 gtl::ArraySlice<int64_t> new_sizes,
747 Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const;
748 };
749
750 // Implementation details
751
752 // START_SKIP_DOXYGEN
753
754 template <typename T>
base()755 T* Tensor::base() const {
756 return buf_ == nullptr ? nullptr : buf_->base<T>();
757 }
758
759 // This routine is defined out of line for code-space savings
760 template <typename T, size_t NDIMS>
tensor()761 typename TTypes<T, NDIMS>::Tensor Tensor::tensor() {
762 CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
763 return typename TTypes<T, NDIMS>::Tensor(base<T>(),
764 shape().AsEigenDSizes<NDIMS>());
765 }
766
767 // This routine is defined out of line for code-space savings
768 template <typename T, size_t NDIMS>
tensor()769 typename TTypes<T, NDIMS>::ConstTensor Tensor::tensor() const {
770 CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
771 return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(),
772 shape().AsEigenDSizes<NDIMS>());
773 }
774
775 template <typename T, size_t NDIMS>
bit_casted_tensor()776 typename TTypes<T, NDIMS>::Tensor Tensor::bit_casted_tensor() {
777 CHECK(IsAligned());
778 return typename TTypes<T, NDIMS>::Tensor(base<T>(),
779 shape().AsEigenDSizes<NDIMS>());
780 }
781
782 template <typename T, size_t NDIMS>
bit_casted_tensor()783 typename TTypes<T, NDIMS>::ConstTensor Tensor::bit_casted_tensor() const {
784 CHECK(IsAligned());
785 return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(),
786 shape().AsEigenDSizes<NDIMS>());
787 }
788
789 template <typename T, size_t NDIMS>
reinterpret_last_dimension()790 typename TTypes<T, NDIMS>::Tensor Tensor::reinterpret_last_dimension() {
791 if (NDIMS == dims()) {
792 return tensor<T, NDIMS>();
793 }
794 CHECK(IsAligned());
795 CHECK_EQ(static_cast<int>(NDIMS), dims() - 1);
796 CHECK_EQ(static_cast<int64_t>(sizeof(T)),
797 shape_.dim_sizes()[NDIMS] * DataTypeSize(dtype()));
798 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
799 for (int d = 0; d < NDIMS; ++d) {
800 dims[d] = shape_.dim_sizes()[d];
801 }
802 return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
803 }
804
805 template <typename T, size_t NDIMS>
reinterpret_last_dimension()806 typename TTypes<T, NDIMS>::ConstTensor Tensor::reinterpret_last_dimension()
807 const {
808 if (NDIMS == dims()) {
809 return tensor<T, NDIMS>();
810 }
811 CHECK(IsAligned());
812 CHECK_EQ(static_cast<int>(NDIMS), dims() - 1);
813 CHECK_EQ(static_cast<int64_t>(sizeof(T)),
814 shape_.dim_sizes()[NDIMS] * DataTypeSize(dtype()));
815 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
816 for (int d = 0; d < NDIMS; ++d) {
817 dims[d] = shape_.dim_sizes()[d];
818 }
819 return typename TTypes<T, NDIMS>::ConstTensor(base<const T>(), dims);
820 }
821
822 template <size_t NDIMS>
FillDimsAndValidateCompatibleShape(gtl::ArraySlice<int64_t> new_sizes,Eigen::array<Eigen::DenseIndex,NDIMS> * dims)823 void Tensor::FillDimsAndValidateCompatibleShape(
824 gtl::ArraySlice<int64_t> new_sizes,
825 Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const {
826 CHECK_EQ(NDIMS, new_sizes.size());
827 int64_t new_num_elements = 1;
828 for (size_t d = 0; d < NDIMS; d++) {
829 new_num_elements *= new_sizes[d];
830 (*dims)[d] = new_sizes[d];
831 }
832 CHECK_EQ(new_num_elements, NumElements());
833 }
834
835 template <typename T, size_t NDIMS>
FillDimsAndValidateCompatibleShape(gtl::ArraySlice<int64_t> new_sizes,Eigen::array<Eigen::DenseIndex,NDIMS> * dims)836 void Tensor::FillDimsAndValidateCompatibleShape(
837 gtl::ArraySlice<int64_t> new_sizes,
838 Eigen::array<Eigen::DenseIndex, NDIMS>* dims) const {
839 CHECK_EQ(NDIMS, new_sizes.size());
840 int64_t new_num_elements = 1;
841 for (size_t d = 0; d < NDIMS; d++) {
842 new_num_elements *= new_sizes[d];
843 (*dims)[d] = new_sizes[d];
844 }
845 const int element_size = DataTypeSize(BaseType(dtype()));
846 if (element_size > 0) {
847 CHECK_EQ(new_num_elements * static_cast<int64_t>(sizeof(T)),
848 NumElements() * element_size);
849 } else {
850 // DataTypeSize() returns 0 for some data types. In this case, assume that T
851 // has the same size as the buffer type.
852 // NOTE: If we can be sure that DataTypeSize() does not return 0 for all POD
853 // types, then we should check DataTypeToEnum<T>::v() == dtype(). Or simply
854 // check if `element_size > 0` to err when bit cast is attempted on Tensor
855 // of unknown data type size.
856 CHECK_EQ(new_num_elements, NumElements());
857 }
858 }
859
860 template <typename T>
flat()861 typename TTypes<T>::Flat Tensor::flat() {
862 // Equivalent to 'return shaped<T, 1>({NumElements()});'
863 CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
864 Eigen::array<Eigen::DenseIndex, 1> dims;
865 dims[0] = NumElements();
866 return typename TTypes<T, 1>::Tensor(base<T>(), dims);
867 }
868
869 template <typename T>
flat()870 typename TTypes<T>::ConstFlat Tensor::flat() const {
871 // Equuivalent to 'return shaped<T, 1>({NumElements()});'
872 CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
873 Eigen::array<Eigen::DenseIndex, 1> dims;
874 dims[0] = NumElements();
875 return typename TTypes<T, 1>::ConstTensor(base<T>(), dims);
876 }
877
878 template <typename T, size_t NDIMS>
shaped(gtl::ArraySlice<int64_t> new_sizes)879 typename TTypes<T, NDIMS>::Tensor Tensor::shaped(
880 gtl::ArraySlice<int64_t> new_sizes) {
881 CheckTypeAndIsAligned(DataTypeToEnum<T>::v());
882 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
883 FillDimsAndValidateCompatibleShape(new_sizes, &dims);
884 return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
885 }
886
887 template <typename T, size_t NDIMS>
bit_casted_shaped(gtl::ArraySlice<int64_t> new_sizes)888 typename TTypes<T, NDIMS>::Tensor Tensor::bit_casted_shaped(
889 gtl::ArraySlice<int64_t> new_sizes) {
890 CHECK(IsAligned());
891 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
892 FillDimsAndValidateCompatibleShape<T>(new_sizes, &dims);
893 return typename TTypes<T, NDIMS>::Tensor(base<T>(), dims);
894 }
895
896 template <typename T, size_t NDIMS>
unaligned_shaped(gtl::ArraySlice<int64_t> new_sizes)897 typename TTypes<T, NDIMS>::UnalignedTensor Tensor::unaligned_shaped(
898 gtl::ArraySlice<int64_t> new_sizes) {
899 CheckType(DataTypeToEnum<T>::v());
900 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
901 FillDimsAndValidateCompatibleShape(new_sizes, &dims);
902 return typename TTypes<T, NDIMS>::UnalignedTensor(base<T>(), dims);
903 }
904
905 template <typename T, size_t NDIMS>
shaped(gtl::ArraySlice<int64_t> new_sizes)906 typename TTypes<T, NDIMS>::ConstTensor Tensor::shaped(
907 gtl::ArraySlice<int64_t> new_sizes) const {
908 CheckType(DataTypeToEnum<T>::v());
909 CHECK(IsAligned()) << "ptr = " << base<void>();
910 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
911 FillDimsAndValidateCompatibleShape(new_sizes, &dims);
912 return typename TTypes<T, NDIMS>::ConstTensor(base<T>(), dims);
913 }
914
915 template <typename T, size_t NDIMS>
bit_casted_shaped(gtl::ArraySlice<int64_t> new_sizes)916 typename TTypes<T, NDIMS>::ConstTensor Tensor::bit_casted_shaped(
917 gtl::ArraySlice<int64_t> new_sizes) const {
918 CHECK(IsAligned());
919 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
920 FillDimsAndValidateCompatibleShape<T>(new_sizes, &dims);
921 return typename TTypes<T, NDIMS>::ConstTensor(base<T>(), dims);
922 }
923
924 template <typename T, size_t NDIMS>
unaligned_shaped(gtl::ArraySlice<int64_t> new_sizes)925 typename TTypes<T, NDIMS>::UnalignedConstTensor Tensor::unaligned_shaped(
926 gtl::ArraySlice<int64_t> new_sizes) const {
927 CheckType(DataTypeToEnum<T>::v());
928 Eigen::array<Eigen::DenseIndex, NDIMS> dims;
929 FillDimsAndValidateCompatibleShape(new_sizes, &dims);
930 return typename TTypes<T, NDIMS>::UnalignedConstTensor(base<T>(), dims);
931 }
932
933 template <typename T>
scalar()934 typename TTypes<T>::Scalar Tensor::scalar() {
935 static_assert(
936 !std::is_same<T, std::string>::value,
937 "std::string is no longer a scalar type, use tensorflow::tstring");
938 CheckIsAlignedAndSingleElement();
939 return typename TTypes<T>::Scalar(base<T>());
940 }
941
942 template <typename T>
scalar()943 typename TTypes<T>::ConstScalar Tensor::scalar() const {
944 static_assert(
945 !std::is_same<T, std::string>::value,
946 "std::string is no longer a scalar type, use tensorflow::tstring");
947 CheckIsAlignedAndSingleElement();
948 return typename TTypes<T>::ConstScalar(base<T>());
949 }
950
951 template <typename T, size_t NDIMS>
flat_inner_dims()952 typename TTypes<T, NDIMS>::Tensor Tensor::flat_inner_dims() {
953 return shaped<T, NDIMS>(ComputeFlatInnerDims(shape_.dim_sizes(), NDIMS));
954 }
955
956 template <typename T, size_t NDIMS>
flat_outer_dims()957 typename TTypes<T, NDIMS>::Tensor Tensor::flat_outer_dims() {
958 return shaped<T, NDIMS>(ComputeFlatOuterDims(shape_.dim_sizes(), NDIMS));
959 }
960
961 template <typename T, size_t NDIMS>
flat_inner_outer_dims(int64_t begin)962 typename TTypes<T, NDIMS>::Tensor Tensor::flat_inner_outer_dims(int64_t begin) {
963 gtl::InlinedVector<int64_t, 4> flat_outer =
964 ComputeFlatOuterDims(shape_.dim_sizes(), begin + NDIMS);
965 return shaped<T, NDIMS>(ComputeFlatInnerDims(flat_outer, NDIMS));
966 }
967
968 template <typename T, size_t NDIMS>
flat_inner_dims()969 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_inner_dims() const {
970 return shaped<T, NDIMS>(ComputeFlatInnerDims(shape_.dim_sizes(), NDIMS));
971 }
972
973 template <typename T, size_t NDIMS>
flat_outer_dims()974 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_outer_dims() const {
975 return shaped<T, NDIMS>(ComputeFlatOuterDims(shape_.dim_sizes(), NDIMS));
976 }
977
978 template <typename T, size_t NDIMS>
flat_inner_outer_dims(int64_t begin)979 typename TTypes<T, NDIMS>::ConstTensor Tensor::flat_inner_outer_dims(
980 int64_t begin) const {
981 gtl::InlinedVector<int64_t, 4> flat_outer =
982 ComputeFlatOuterDims(shape_.dim_sizes(), begin + NDIMS);
983 return shaped<T, NDIMS>(ComputeFlatInnerDims(flat_outer, NDIMS));
984 }
985
Tensor(const Tensor & other)986 inline Tensor::Tensor(const Tensor& other)
987 : shape_(other.shape()), buf_(other.buf_) {
988 if (buf_) buf_->Ref();
989 }
990
Tensor(Tensor && other)991 inline Tensor::Tensor(Tensor&& other)
992 : shape_(std::move(other.shape_)), buf_(other.buf_) {
993 other.buf_ = nullptr;
994 }
995
996 class Tensor::HostScalarTensorBufferBase : public TensorBuffer {
997 public:
998 using TensorBuffer::TensorBuffer;
999 bool GetAllocatedBytes(size_t* out_bytes) const final;
1000 void FillAllocationDescription(AllocationDescription* proto) const final;
1001 };
1002
1003 // A packed representation for a single scalar value of type `T`, and a
1004 // `TensorBuffer` implementation that describes (and manages the lifetime of)
1005 // that value.
1006 template <typename T>
1007 struct Tensor::ValueAndTensorBuffer {
1008 class HostScalarTensorBuffer : public Tensor::HostScalarTensorBufferBase {
1009 public:
HostScalarTensorBufferValueAndTensorBuffer1010 explicit HostScalarTensorBuffer(void* data)
1011 : HostScalarTensorBufferBase(data) {}
sizeValueAndTensorBuffer1012 size_t size() const final { return sizeof(T); }
root_bufferValueAndTensorBuffer1013 TensorBuffer* root_buffer() final { return this; }
1014
1015 // Override `operator delete` so that calling `delete this` in
1016 // `core::Refcounted::Unref()` for an object of this type will free
1017 // the enclosing `ValueAndTensorBuffer` for the tensor buffer.
1018 //
1019 // NOTE(mrry): The definition of this method must be outside the class
1020 // definition in order to satisfy some compilers.
1021 static void operator delete(void* ptr);
1022
deleteValueAndTensorBuffer1023 static void operator delete(void*, void*) {
1024 // Some compilers require an overridden class-specific deallocation
1025 // function, which will be called if placement `new` throws an
1026 // exception.
1027 }
1028
1029 private:
~HostScalarTensorBufferValueAndTensorBuffer1030 ~HostScalarTensorBuffer() override { static_cast<T*>(data())->~T(); }
1031 };
1032
1033 T value;
1034 HostScalarTensorBuffer tensor_buffer;
1035 };
1036
1037 /* static */
1038 template <typename T>
delete(void * ptr)1039 void Tensor::ValueAndTensorBuffer<T>::HostScalarTensorBuffer::operator delete(
1040 void* ptr) {
1041 // Use a dummy object to compute to offset of
1042 // `ValueAndTensorBuffer::tensor_buffer`, because `offsetof()` is not
1043 // necessarily defined on this non-POD type (until C++17).
1044 //
1045 // NOTE(mrry): Using `sizeof(Tensor::ValueAndTensorBuffer<T>)` here requires
1046 // us to define this method outside the class definition, so that it is not
1047 // considered an incomplete type.
1048 typename std::aligned_storage<sizeof(Tensor::ValueAndTensorBuffer<T>),
1049 alignof(Tensor::ValueAndTensorBuffer<T>)>::type
1050 dummy_storage_;
1051 Tensor::ValueAndTensorBuffer<T>* dummy_object =
1052 reinterpret_cast<Tensor::ValueAndTensorBuffer<T>*>(&dummy_storage_);
1053 intptr_t offset = reinterpret_cast<intptr_t>(&dummy_object->tensor_buffer) -
1054 reinterpret_cast<intptr_t>(dummy_object);
1055
1056 port::AlignedFree(static_cast<char*>(ptr) - offset);
1057 }
1058
1059 template <typename T>
Tensor(T value,host_scalar_tag tag)1060 Tensor::Tensor(T value, host_scalar_tag tag) {
1061 auto* value_and_buf = static_cast<Tensor::ValueAndTensorBuffer<T>*>(
1062 port::AlignedMalloc(sizeof(typename Tensor::ValueAndTensorBuffer<T>),
1063 EIGEN_MAX_ALIGN_BYTES));
1064 new (&value_and_buf->value) T(std::move(value));
1065 new (&value_and_buf->tensor_buffer)
1066 typename Tensor::ValueAndTensorBuffer<T>::HostScalarTensorBuffer(
1067 value_and_buf);
1068 buf_ = &value_and_buf->tensor_buffer;
1069 set_dtype(DataTypeToEnum<T>::value);
1070 }
1071
1072 inline Tensor& Tensor::operator=(Tensor&& other) {
1073 // Avoid self-assignment, since we might destroy our underlying buffer.
1074 if (&other != this) {
1075 shape_ = std::move(other.shape_);
1076 if (buf_) buf_->Unref();
1077 buf_ = other.buf_;
1078 other.buf_ = nullptr;
1079 }
1080 return *this;
1081 }
1082
1083 // END_SKIP_DOXYGEN
1084
1085 } // namespace tensorflow
1086
1087 #endif // TENSORFLOW_CORE_FRAMEWORK_TENSOR_H_
1088