2 // Copyright (c) 2016-2018 Intel Corporation
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
17 ///////////////////////////////////////////////////////////////////////////////////////////////////
25 /// @addtogroup cpp_api C++ API
28 /// @addtogroup cpp_memory Memory description and management
31 /// @brief Possible data types could be stored in memory.
32 enum class data_types : size_t
34 i8 = cldnn_i8,/// Not supported in current HW
42 class optional_data_type
44 // Must be the same as the undrelying type of `data_types`.
45 using storage_type = size_t;
47 // Implicitly assumes that this value is not used in the `data_types`.
48 static constexpr auto non_specified_type =
49 std::numeric_limits<storage_type>::max();
53 : storage(non_specified_type)
56 optional_data_type(data_types type)
57 : storage(static_cast<storage_type>(type))
60 operator bool() const { return storage != non_specified_type; }
62 // Similarly to std::optional does *not* verify that the object has the type
63 // set. Unlike it, though, returns the value instead of pointer/reference.
64 data_types operator*() const { return static_cast<data_types>(storage); }
66 optional_data_type& operator=(const data_types new_type)
68 storage = static_cast<storage_type>(new_type);
76 /// Converts C++ type to @ref data_types .
77 template <typename T> struct type_to_data_type;
78 #ifndef DOXYGEN_SHOULD_SKIP_THIS
79 template<> struct type_to_data_type<int8_t> { static const data_types value = data_types::i8; };
80 template<> struct type_to_data_type<uint8_t> { static const data_types value = data_types::u8; };
81 template<> struct type_to_data_type<int32_t> { static const data_types value = data_types::i32; };
82 template<> struct type_to_data_type<int64_t> { static const data_types value = data_types::i64; };
83 template<> struct type_to_data_type<half_t> { static const data_types value = data_types::f16; };
84 template<> struct type_to_data_type<float> { static const data_types value = data_types::f32; };
87 /// Converts @ref data_types to C++ type.
88 template<data_types Data_Type> struct data_type_to_type;
89 #ifndef DOXYGEN_SHOULD_SKIP_THIS
90 template<> struct data_type_to_type<data_types::i8> { typedef int8_t type; };
91 template<> struct data_type_to_type<data_types::i32> { typedef int32_t type; };
92 template<> struct data_type_to_type<data_types::i64> { typedef int64_t type; };
93 template<> struct data_type_to_type<data_types::f16> { typedef half_t type; };
94 template<> struct data_type_to_type<data_types::f32> { typedef float type; };
98 /// Helper class to identify key properties for data_types.
99 struct data_type_traits
101 static size_t size_of(data_types data_type)
103 return (static_cast<uint32_t>(data_type) & ~(CLDNN_FLOAT_TYPE_MASK | CLDNN_UINT_TYPE_MASK));
106 static bool is_floating_point(data_types data_type)
108 return (static_cast<uint32_t>(data_type) & CLDNN_FLOAT_TYPE_MASK) != 0;
111 static size_t align_of(data_types data_type)
116 return alignof(data_type_to_type<data_types::i8>::type);
117 case data_types::i32:
118 return alignof(data_type_to_type<data_types::i32>::type);
119 case data_types::i64:
120 return alignof(data_type_to_type<data_types::i64>::type);
121 case data_types::f16:
122 return alignof(data_type_to_type<data_types::f16>::type);
123 case data_types::f32:
124 return alignof(data_type_to_type<data_types::f32>::type);
125 default: return size_t(1);
129 static std::string name(data_types data_type)
137 case data_types::i32:
139 case data_types::i64:
141 case data_types::f16:
143 case data_types::f32:
147 return std::string("invalid data type: " + std::to_string((int)data_type));
152 /// Helper function to check if C++ type matches @p data_type.
153 template <typename T>
154 bool data_type_match(data_types data_type)
156 return data_type == type_to_data_type<T>::value;
159 /// Helper function to get both data_types and format::type in a single, unique value. Useable in 'case' statement.
160 constexpr auto fuse(data_types dt, cldnn::format::type fmt) -> decltype(static_cast<std::underlying_type<data_types>::type>(dt) | static_cast<std::underlying_type<format::type>::type>(fmt))
162 using dt_type = std::underlying_type<data_types>::type;
163 using fmt_type = std::underlying_type<cldnn::format::type>::type;
164 using fmt_narrow_type = int16_t;
166 return static_cast<fmt_type>(fmt) <= std::numeric_limits<fmt_narrow_type>::max() &&
167 static_cast<dt_type>(dt) <= (std::numeric_limits<dt_type>::max() >> (sizeof(fmt_narrow_type) * 8))
168 ? (static_cast<dt_type>(dt) << (sizeof(fmt_narrow_type) * 8)) |
169 (static_cast<fmt_type>(fmt) >= 0 ? static_cast<fmt_narrow_type>(fmt) : static_cast<fmt_narrow_type>(-1))
170 : throw std::invalid_argument("data_type and/or format values are too big to be fused into single value");
174 /// @brief Represents data padding information.
177 /// @brief Filling value for padding area.
178 float filling_value() const { return _filling_value; }
180 /// @brief Gets lower padding sizes. For spatials, it means size of left (X) and top (Y) padding.
181 /// @return Tensor with padding for top/left/lower bounds of data.
182 tensor lower_size() const { return _lower_size; }
184 /// @brief Gets upper padding sizes. For spatials, it means size of right (X) and bottom (Y) padding.
185 /// @return Tensor with padding for bottom/right/upper bounds of data.
186 tensor upper_size() const { return _upper_size; }
189 /// @param lower_sizes Top-left padding sizes. See @ref tensor::tensor(const std::vector<value_type>&, value_type) for details.
190 /// @param upper_sizes Bottom-right padding sizes. See @ref tensor::tensor(const std::vector<value_type>&, value_type) for details.
191 /// @param filling_value Filling value for padding area.
192 padding(const std::vector<tensor::value_type>& lower_sizes, const std::vector<tensor::value_type>& upper_sizes, float filling_value = 0.0f)
193 : _lower_size(to_abs(lower_sizes), 0), _upper_size(to_abs(upper_sizes), 0), _filling_value(filling_value)
196 /// @brief Constrcuts symmetric padding.
197 /// @param sizes Top-left and bottom-right padding sizes. See @ref tensor::tensor(const std::vector<value_type>&, value_type) for details.
198 /// @param filling_value Filling value for padding area.
199 padding(const std::vector<tensor::value_type>& sizes, float filling_value = 0.0f)
200 : padding(sizes, sizes, filling_value)
203 /// @brief Constructs "zero-sized" padding.
204 padding() : padding({ 0,0,0,0 }, 0) {}
206 /// @brief Copy construction.
207 padding(const cldnn_padding& other)
208 : _lower_size(other.lower_size), _upper_size(other.upper_size), _filling_value(other.filling_value)
211 /// @brief Implicit conversion to C API @ref cldnn_padding.
212 operator cldnn_padding() const
214 return{ static_cast<cldnn_tensor>(_lower_size),
215 static_cast<cldnn_tensor>(_upper_size),
219 /// @brief Returns true if padding size is not zero.
220 explicit operator bool() const
222 return std::any_of(_lower_size.raw.begin(), _lower_size.raw.end(), [](const tensor::value_type& el) { return el != 0; }) ||
223 std::any_of(_upper_size.raw.begin(), _upper_size.raw.end(), [](const tensor::value_type& el) { return el != 0; });
226 friend bool operator ==(const padding& lhs, const padding& rhs)
228 return lhs._lower_size == rhs._lower_size
229 && lhs._upper_size == rhs._upper_size
230 && lhs._filling_value == rhs._filling_value;
233 friend bool operator !=(const padding& lhs, const padding& rhs)
235 return !(lhs == rhs);
238 friend bool operator <(const padding& lhs, const padding& rhs)
240 if (lhs._filling_value != rhs._filling_value)
241 return (lhs._filling_value < rhs._filling_value);
242 if (lhs._lower_size != rhs._lower_size)
243 return (lhs._lower_size < rhs._lower_size);
244 return (lhs._upper_size < rhs._upper_size);
247 static padding max(padding const& lhs, padding const& rhs, float filling_value = 0.0f)
249 auto lower = tensor::max(lhs.lower_size(), rhs.lower_size());
250 auto upper = tensor::max(lhs.upper_size(), rhs.upper_size());
251 return padding{ lower.sizes(), upper.sizes(), filling_value };
255 tensor _lower_size; ///< Lower padding sizes. For spatials, it means size of left (X) and top (Y) padding.
256 tensor _upper_size; ///< Upper padding sizes. For spatials, it means size of right (X) and bottom (Y) padding.
257 // TODO: Add support for non-zero filling value (if necessary) or remove variable (if not necessary).
258 float _filling_value; ///< Filling value for an element of padding. If data type of elements is different than float it is converted
259 ///< to it using round-towards-nearest-even (for floating-point data types) or round-towards-zero (for integral
262 static std::vector<tensor::value_type> to_abs(const std::vector<tensor::value_type>& sizes)
264 std::vector<tensor::value_type> result;
265 result.reserve(sizes.size());
266 std::transform(sizes.cbegin(), sizes.cend(), std::back_inserter(result), [](const tensor::value_type& el) { return abs(el); });
267 return result; // NRVO
271 /// @brief Describes memory layout.
272 /// @details Contains information about data stored in @ref memory.
275 /// Constructs layout based on @p data_type and @p size information described by @ref tensor
276 layout(data_types data_type, cldnn::format fmt, tensor size, padding apadding = padding())
277 : data_type(data_type)
280 , data_padding(apadding)
283 /// Construct C++ layout based on C API @p cldnn_layout
284 layout(const cldnn_layout& other)
285 : data_type(static_cast<data_types>(other.data_type))
286 , format(static_cast<cldnn::format::type>(other.format))
288 , data_padding(other.padding)
291 /// Convert to C API @p cldnn_layout
292 operator cldnn_layout() const
294 return{ static_cast<decltype(cldnn_layout::data_type)>(data_type), static_cast<decltype(cldnn_layout::format)>(format), size, data_padding };
297 layout(const layout& other) = default;
299 layout& operator=(const layout& other)
303 data_type = other.data_type;
304 format = other.format;
306 data_padding = other.data_padding;
310 friend bool operator==(const layout& lhs, const layout& rhs)
312 return lhs.data_type == rhs.data_type
313 && lhs.format == rhs.format
314 && lhs.size == rhs.size
315 && lhs.data_padding == rhs.data_padding;
318 friend bool operator!=(const layout& lhs, const layout& rhs)
320 return !(lhs == rhs);
323 friend bool operator<(const layout& lhs, const layout& rhs)
325 if (lhs.data_type != rhs.data_type)
326 return (lhs.data_type < rhs.data_type);
327 if (lhs.format != rhs.format)
328 return (lhs.format < rhs.format);
329 if (lhs.size < rhs.size)
330 return (lhs.size < rhs.size);
331 return (lhs.data_padding < rhs.data_padding);
334 /// Number of elements to be stored in this memory layout
335 size_t count() const { return size.count(); }
337 /// Layout size with padding included
338 tensor get_buffer_size() const
340 return size.add(data_padding.lower_size()).add(data_padding.upper_size());
343 tensor get_pitches() const
345 auto sizes = get_buffer_size().sizes(format);
346 if (format == format::byxf_af32)
348 sizes[3] = align_to(sizes[3], 32);
351 if (format == format::byx8_f4)
353 sizes[3] = align_to(sizes[3], 4);
354 sizes[2] = align_to(sizes[2], 8);
356 std::vector<tensor::value_type> pitches(sizes.size(), tensor::value_type(1));
357 std::partial_sum(sizes.rbegin(), sizes.rend() - 1, pitches.rbegin() + 1, std::multiplies<tensor::value_type>());
358 return{ format, pitches };
361 // @brief Calculates position within buffer of the data element pointed by the provided tensor.
362 // element == { 0,0,0,0 } means first no-padding (i.e. data) element
363 size_t get_linear_offset(tensor element = { 0,0,0,0 }) const
365 auto pitches = get_pitches();
366 auto l_padd = data_padding.lower_size();
367 auto u_padd = data_padding.upper_size();
369 if ((element.batch[0] < 0 && -element.batch[0] > l_padd.batch[0]) ||
370 (element.feature[0] < 0 && -element.feature[0] > l_padd.feature[0]) ||
371 (element.spatial[0] < 0 && -element.spatial[0] > l_padd.spatial[0]) ||
372 (element.spatial[1] < 0 && -element.spatial[1] > l_padd.spatial[1]) ||
373 (element.batch[0] >= size.batch[0] + u_padd.batch[0]) ||
374 (element.feature[0] >= size.feature[0] + u_padd.feature[0]) ||
375 (element.spatial[0] >= size.spatial[0] + u_padd.spatial[0]) ||
376 (element.spatial[1] >= size.spatial[1] + u_padd.spatial[1]))
377 throw std::invalid_argument("Requested to calculate linear offset for an element which lies outside of the buffer range.");
379 size_t linear_offset =
380 static_cast<size_t>(element.batch[0] + l_padd.batch[0]) * static_cast<size_t>(pitches.batch[0]) +
381 static_cast<size_t>(element.feature[0] + l_padd.feature[0]) * static_cast<size_t>(pitches.feature[0]) +
382 static_cast<size_t>(element.spatial[0] + l_padd.spatial[0]) * static_cast<size_t>(pitches.spatial[0]) +
383 static_cast<size_t>(element.spatial[1] + l_padd.spatial[1]) * static_cast<size_t>(pitches.spatial[1]);
385 return linear_offset;
388 /// @brief Get aligned linear size calculated as multiplication of all elements.
389 size_t get_linear_size() const
391 auto sizes = get_buffer_size().sizes();
392 if (this->format == cldnn::format::os_iyx_osv16 && !is_aligned_to(sizes[0], 16))
394 sizes[0] = align_to(sizes[0], 16);
396 else if (this->format == cldnn::format::os_iyx_osv32 && !is_aligned_to(sizes[0], 32))
398 sizes[0] = align_to(sizes[0], 32);
400 else if (this->format == cldnn::format::os_iyx_osv64 && !is_aligned_to(sizes[0], 64))
402 sizes[0] = align_to(sizes[0], 64);
404 else if (this->format == cldnn::format::bs_xs_xsv8_bsv8 && !(is_aligned_to(sizes[0], 8) && is_aligned_to(sizes[2], 8)))
406 sizes[0] = align_to(sizes[0], 8);
407 sizes[2] = align_to(sizes[2], 8);
409 else if (this->format == cldnn::format::bs_xs_xsv8_bsv16 && !(is_aligned_to(sizes[0], 16) && is_aligned_to(sizes[2], 8)))
411 sizes[0] = align_to(sizes[0], 16);
412 sizes[2] = align_to(sizes[2], 8);
414 else if (this->format == cldnn::format::bs_x_bsv16 && !is_aligned_to(sizes[0], 16))
416 sizes[0] = align_to(sizes[0], 16);
418 else if (this->format == cldnn::format::bf8_xy16 && !(is_aligned_to(sizes[1], 8) && is_aligned_to(sizes[2] * sizes[3], 16)))
420 sizes[1] = align_to(sizes[1], 8);
421 sizes[3] = align_to(sizes[2]*sizes[3], 16);
424 else if (this->format == cldnn::format::byxf_af32 && !(is_aligned_to(sizes[1], 32)))
426 sizes[1] = align_to(sizes[1], 32);
428 else if (this->format == cldnn::format::byx8_f4 && (!is_aligned_to(sizes[1], 4) || !is_aligned_to(sizes[2], 8)))
430 // for this case we want to make sure, that with padding we're aligned to 8 in x
431 auto lp = data_padding.lower_size().spatial[0];
432 auto up = data_padding.upper_size().spatial[0];
433 sizes[1] = align_to(sizes[1], 4);
434 sizes[2] = align_to(lp + up + sizes[2], 8);
437 else if (this->format == cldnn::format::fs_bs_yx_bsv4_fsv32 && (!(is_aligned_to(sizes[1], 32)) || !(is_aligned_to(sizes[0], 4)) ) )
439 sizes[1] = align_to(sizes[1], 32);
440 sizes[0] = align_to(sizes[0], 4);
442 else if (this->format == cldnn::format::b_fs_yx_fsv4 && !(is_aligned_to(sizes[1], 4)))
444 sizes[1] = align_to(sizes[1], 4);
446 else if (this->format == cldnn::format::os_is_yx_isa8_osv8_isv4 && !(is_aligned_to(sizes[0], 8)) && !(is_aligned_to(sizes[1], 32)))
448 sizes[0] = align_to(sizes[0], 8);
449 sizes[1] = align_to(sizes[1], 32);
451 else if (this->format == cldnn::format::os_is_yx_isa8_osv8_isv4_swizzled_by_4 && !(is_aligned_to(sizes[0], 32)) && !(is_aligned_to(sizes[1], 32)))
453 sizes[0] = align_to(sizes[0], 32);
454 sizes[1] = align_to(sizes[1], 32);
456 else if (this->format == cldnn::format::is_o_yx_isv32 && !(is_aligned_to(sizes[1], 32)))
458 sizes[1] = align_to(sizes[1], 32);
460 else if (this->format == cldnn::format::is_o32_yx_isv32_swizzled_by_4 && (!is_aligned_to(sizes[1], 32) || !(is_aligned_to(sizes[0], 32))))
462 sizes[0] = align_to(sizes[0], 32);
463 sizes[1] = align_to(sizes[1], 32);
465 else if (this->format == cldnn::format::os_is_y_x8_osv8_isv4)
467 sizes[1] = align_to(sizes[1], 4);
468 sizes[0] = align_to(sizes[0], 8);
469 sizes[2] = align_to(sizes[2], 8);
471 return std::accumulate(
474 static_cast<size_t>(1),
475 std::multiplies<size_t>()
479 /// Modify padding in layout
480 layout with_padding(padding const& padd) const
483 ret.data_padding = padd;
487 /// Data type stored in @ref memory (see. @ref data_types)
488 data_types data_type;
490 /// Format stored in @ref memory (see. @ref format)
491 cldnn::format format;
493 /// The size of the @ref memory (excluding padding)
496 /// Explicit padding of the @ref memory
497 padding data_padding;
499 /// Number of bytes needed to store this layout
500 size_t bytes_count() const { return data_type_traits::size_of(data_type) * get_linear_size(); }
502 bool has_fused_format(data_types const& dt, cldnn::format const& fmt) const
504 return (data_type == dt && format == fmt);
507 auto fused_format() const -> decltype(fuse(data_type, format))
509 return fuse(data_type, format);