GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
92 / 136
67.6%
Branches:
112 / 252
44.4%
Functions:
6 / 9, 7 excl
66.7%

util/tensor.h
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file tensor.h
3 /// @brief Strided tensors, Ernie's end of DLPack.
4 #pragma once
5
6 #include "ernie/export.h"
7
8 #include <dlpack/dlpack.h>
9
10 #include <algorithm>
11 #include <cstddef>
12 #include <cstdint>
13 #include <memory>
14 #include <span>
15 #include <stdexcept>
16 #include <string_view>
17 #include <type_traits>
18 #include <vector>
19
20 namespace ernie {
21
22 /// @brief Element types Ernie exchanges.
23 enum class DType : std::uint8_t { UInt8, UInt16, UInt32, Int32, Int64, Float16, Float32, Float64 };
24
25 /// @brief The size of an element type.
26 /// @param type The type.
27 /// @return Bytes per element.
28 ERNIE_API std::size_t ByteSize(DType type);
29
30 /// @brief Where a tensor's memory lives: host, CUDA or ROCm (HIP).
31 enum class DeviceKind : std::uint8_t { Cpu, Cuda, Rocm };
32
33 /// @param kind The memory kind.
34 /// @return Whether it is a GPU compute device's memory.
35 constexpr bool IsGpu(DeviceKind kind) {
36 return kind != DeviceKind::Cpu;
37 }
38
39 /// @brief A memory location: a kind and a device index.
40
3/4
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 572 times.
✗ Branch 5 not taken.
3207 struct Device {
41 /// The kind of memory.
42 2 DeviceKind kind = DeviceKind::Cpu;
43 /// The device index within its kind.
44 2 int id = 0;
45
46
2/8
<unknown function 40>:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
<unknown function 42>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 bool operator==(const Device&) const = default;
47 };
48
49 /// @brief A strided n-dimensional view of typed memory, with an
50 /// optional owner keeping it alive.
51 /// Shapes and strides are in elements; empty strides mean row-major
52 /// contiguous. Copies share memory and owner. DLPack tensors are
53 /// adopted and exported without copying.
54 ///
55 /// References:
56 /// - DLPack, https://dmlc.github.io/dlpack/latest/
57 class ERNIE_API Tensor {
58 public:
59
3/4
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 572 times.
✗ Branch 5 not taken.
3061 Tensor() = default;
60
61 /// @brief Views memory.
62 /// @param data The first element.
63 /// @param type The element type.
64 /// @param shape The extent of each dimension.
65 /// @param strides Strides in elements; empty for contiguous.
66 /// @param device Where the memory lives.
67 /// @param owner Kept alive as long as any copy of the view.
68 /// @throws std::invalid_argument If a dimension is negative or
69 /// strides do not match the rank.
70 Tensor(void* data, DType type, std::vector<std::int64_t> shape,
71 std::vector<std::int64_t> strides = {}, Device device = {},
72 std::shared_ptr<const void> owner = {});
73
74 /// @brief Allocates a zero-filled contiguous CPU tensor.
75 /// @param type The element type.
76 /// @param shape The extent of each dimension.
77 /// @return The owning tensor.
78 static Tensor Zeros(DType type, std::vector<std::int64_t> shape);
79
80 /// @brief Copies values into a new contiguous CPU tensor.
81 /// @param values The elements, row-major.
82 /// @param shape The extent of each dimension.
83 /// @return The owning tensor.
84 /// @throws std::invalid_argument If the shape's element count
85 /// differs from `values.size()`.
86 template <typename T>
87 static Tensor Copy(std::span<const T> values, std::vector<std::int64_t> shape);
88
89 /// @brief Adopts a DLPack tensor without copying.
90 /// Its deleter runs when the last referencing `Tensor` goes.
91 /// @param managed The producer's tensor.
92 /// @return The tensor.
93 /// @throws std::invalid_argument For an unsupported type, lanes
94 /// or device; the deleter still runs.
95 static Tensor FromDlpack(DLManagedTensorVersioned* managed);
96 /// @brief Adopts an unversioned DLPack tensor, as above.
97 /// @param managed The producer's tensor.
98 /// @return The tensor.
99 static Tensor FromDlpack(DLManagedTensor* managed);
100
101 /// @brief Exports to DLPack without copying.
102 /// @return A tensor keeping the memory alive until the consumer
103 /// calls its deleter.
104 DLManagedTensorVersioned* ToDlpack() const;
105
106 /// @return Whether it views no memory.
107 214 bool Empty() const {
108
9/10
<unknown function 107>:
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 26 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 47 times.
<unknown function 40>:
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
214 return data_ == nullptr;
109 }
110
111 /// @return The first element.
112 28 void* Data() const {
113
15/35
<unknown function 107>:
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
<unknown function 112>:
✓ Branch 1 taken 2 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✗ Branch 5 not taken.
<unknown function 40>:
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 time.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 time.
✗ Branch 15 not taken.
✓ Branch 17 taken 1 time.
✗ Branch 18 not taken.
✓ Branch 20 taken 1 time.
✗ Branch 21 not taken.
<unknown function 42>:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
26 return data_;
114 }
115
116 /// @return The element type.
117 273 DType Type() const {
118
12/28
<unknown function 107>:
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✗ Branch 5 not taken.
<unknown function 112>:
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 5 taken 2 times.
<unknown function 117>:
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 66 times.
<unknown function 40>:
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
<unknown function 42>:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
273 return type_;
119 }
120
121 /// @return Where the memory lives.
122 18 Device Location() const {
123
4/16
<unknown function 107>:
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
<unknown function 112>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
<unknown function 122>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
<unknown function 40>:
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
<unknown function 42>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
18 return device_;
124 }
125 /// @return Whether its memory is the host's, as an empty tensor's is.
126 178 bool OnHost() const {
127
10/14
<unknown function 107>:
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 47 times.
<unknown function 126>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
<unknown function 40>:
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
151 return !IsGpu(device_.kind);
128 }
129
130 /// @return The extent of each dimension.
131 56 const std::vector<std::int64_t>& Shape() const {
132
9/27
<unknown function 107>:
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
<unknown function 112>:
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✓ Branch 1 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 1 time.
<unknown function 117>:
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
<unknown function 126>:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
<unknown function 40>:
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
<unknown function 42>:
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
124 return shape_;
133 }
134
135 /// @return Strides in elements, filled in when contiguous.
136 3 const std::vector<std::int64_t>& Strides() const {
137
3/6
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
3 return strides_;
138 }
139
140 /// @return The number of dimensions.
141 132 std::size_t Rank() const {
142
14/22
<unknown function 107>:
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
<unknown function 112>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 1 time.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 1 time.
<unknown function 117>:
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 61 times.
✓ Branch 4 taken 49 times.
<unknown function 40>:
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
<unknown function 42>:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
197 return shape_.size();
143 }
144
145 /// @return The number of elements.
146 std::int64_t Elements() const;
147 /// @return The elements' bytes.
148 ✗ std::uint64_t Bytes() const {
149 ✗ return static_cast<std::uint64_t>(Elements()) * ByteSize(type_);
150 }
151
152 /// @brief Views one entry along the first dimension.
153 /// @param index The entry.
154 /// @return A view sharing memory and owner.
155 /// @throws std::out_of_range If rank 0 or `index` is out of
156 /// range.
157 Tensor Row(std::int64_t index) const;
158
159 /// @brief Views a range along the first dimension.
160 /// @param first The first entry.
161 /// @param count How many entries.
162 /// @return A view sharing memory and owner.
163 /// @throws std::out_of_range If rank 0 or the range is out of
164 /// range.
165 Tensor Slice(std::int64_t first, std::int64_t count) const;
166
167 /// @brief Views contiguous memory with another shape, on any
168 /// device.
169 /// @param shape The new shape, of as many elements.
170 /// @return A view sharing memory and owner.
171 /// @throws std::invalid_argument If not contiguous or the element
172 /// counts differ.
173 Tensor Reshaped(std::vector<std::int64_t> shape) const;
174
175 /// @return Whether the elements are row-major contiguous.
176 bool IsContiguous() const;
177
178 /// @brief A contiguous CPU tensor of the same elements.
179 /// @return This tensor if already contiguous, else an owning
180 /// copy.
181 /// @throws std::invalid_argument If it is not on the CPU.
182 Tensor Contiguous() const;
183
184 /// @brief Typed access to the first element.
185 /// @return The pointer.
186 /// @throws std::invalid_argument If `T` is not the element type.
187 template <typename T> T* As() const;
188
189 private:
190 void* data_ = nullptr;
191 DType type_ = DType::Float32;
192 std::vector<std::int64_t> shape_;
193 std::vector<std::int64_t> strides_;
194 Device device_;
195 std::shared_ptr<const void> owner_;
196 };
197
198 /// @brief The `DType` of C++ element type `T`.
199 template <typename T>
200 inline constexpr DType kDType = [] {
201 if constexpr (std::is_same_v<T, std::uint8_t>) {
202 return DType::UInt8;
203 } else if constexpr (std::is_same_v<T, std::uint16_t>) {
204 return DType::UInt16;
205 } else if constexpr (std::is_same_v<T, std::uint32_t>) {
206 return DType::UInt32;
207 } else if constexpr (std::is_same_v<T, std::int32_t>) {
208 return DType::Int32;
209 } else if constexpr (std::is_same_v<T, std::int64_t>) {
210 return DType::Int64;
211 } else if constexpr (std::is_same_v<T, float>) {
212 return DType::Float32;
213 } else if constexpr (std::is_same_v<T, double>) {
214 return DType::Float64;
215 } else {
216 static_assert(sizeof(T) == 0, "Not a tensor element type");
217 }
218 }();
219
220 template <typename T>
221
5/10
ernie::Tensor ernie::Tensor::Copy<double>(std::span<double const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<float>(std::span<float const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<int>(std::span<int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned char>(std::span<unsigned char const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned int>(std::span<unsigned int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
128 Tensor Tensor::Copy(std::span<const T> values, std::vector<std::int64_t> shape) {
222
5/10
ernie::Tensor ernie::Tensor::Copy<double>(std::span<double const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<float>(std::span<float const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<int>(std::span<int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned char>(std::span<unsigned char const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned int>(std::span<unsigned int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
128 Tensor tensor = Zeros(kDType<T>, std::move(shape));
223
11/26
ernie::Tensor ernie::Tensor::Copy<double>(std::span<double const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 1 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
ernie::Tensor ernie::Tensor::Copy<float>(std::span<float const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 1 not taken.
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 74 times.
ernie::Tensor ernie::Tensor::Copy<int>(std::span<int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 1 not taken.
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 47 times.
ernie::Tensor ernie::Tensor::Copy<unsigned char>(std::span<unsigned char const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
ernie::Tensor ernie::Tensor::Copy<unsigned int>(std::span<unsigned int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
128 if (static_cast<std::size_t>(tensor.Elements()) != values.size()) {
224
1/10
ernie::Tensor ernie::Tensor::Copy<double>(std::span<double const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
ernie::Tensor ernie::Tensor::Copy<float>(std::span<float const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
ernie::Tensor ernie::Tensor::Copy<int>(std::span<int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned char>(std::span<unsigned char const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned int>(std::span<unsigned int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 throw std::invalid_argument("Tensor::Copy shape does not match the number of values");
225 }
226
5/10
ernie::Tensor ernie::Tensor::Copy<double>(std::span<double const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<float>(std::span<float const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<int>(std::span<int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned char>(std::span<unsigned char const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
ernie::Tensor ernie::Tensor::Copy<unsigned int>(std::span<unsigned int const, 18446744073709551615ul>, std::vector<long, std::allocator<long> >):
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
127 std::copy(values.begin(), values.end(), tensor.As<T>());
227 127 return tensor;
228 1 }
229
230 327 template <typename T> T* Tensor::As() const {
231
1/6
float* ernie::Tensor::As<float>() const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
int* ernie::Tensor::As<int>() const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
unsigned char* ernie::Tensor::As<unsigned char>() const:
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
327 if (kDType<T> != type_) {
232
0/6
float* ernie::Tensor::As<float>() const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
int* ernie::Tensor::As<int>() const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
unsigned char* ernie::Tensor::As<unsigned char>() const:
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 throw std::invalid_argument("Tensor::As with the wrong element type");
233 }
234 326 return static_cast<T*>(data_);
235 }
236
237 /// @brief Copies a CPU tensor of any shape out as floats.
238 /// @param tensor A float32 or float64 tensor on the CPU.
239 /// @param what What it holds, for errors.
240 /// @return Its elements in row-major order; none for an empty tensor.
241 /// @throws std::invalid_argument If it is elsewhere or not float.
242 ERNIE_API std::vector<float> CopyFloats(const Tensor& tensor, std::string_view what);
243
244 /// @brief Copies a CPU array of vectors out as floats.
245 /// @param tensor A flat or (n, `components`) float tensor.
246 /// @param components The vectors' size.
247 /// @param what What it holds, for errors.
248 /// @return Its elements in row-major order; none for an empty tensor.
249 /// @throws std::invalid_argument If its shape or type is wrong.
250 ERNIE_API std::vector<float> CopyFloats(const Tensor& tensor, std::size_t components,
251 std::string_view what);
252
253 /// @brief Copies a CPU tensor of any shape out as integers.
254 /// @param tensor An int32, int64 or uint32 tensor on the CPU.
255 /// @param what What it holds, for errors.
256 /// @return Its elements in row-major order; none for an empty tensor.
257 /// @throws std::invalid_argument If it is elsewhere or not integer.
258 ERNIE_API std::vector<std::int64_t> CopyInts(const Tensor& tensor, std::string_view what);
259
260 } // namespace ernie
261