Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
TensorStriding.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_TENSOR_TENSOR_STRIDING_H
12#define EIGEN_TENSOR_TENSOR_STRIDING_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20template <typename Strides, typename XprType>
21struct traits<TensorStridingOp<Strides, XprType> > : public traits<XprType> {
22 typedef typename XprType::Scalar Scalar;
23 typedef traits<XprType> XprTraits;
24 typedef typename XprTraits::StorageKind StorageKind;
25 typedef typename XprTraits::Index Index;
26 static constexpr int NumDimensions = XprTraits::NumDimensions;
27 static constexpr int Layout = XprTraits::Layout;
28 typedef typename XprTraits::PointerType PointerType;
29};
30
31template <typename Strides, typename XprType>
32struct eval<TensorStridingOp<Strides, XprType>, Eigen::Dense> {
33 typedef const TensorStridingOp<Strides, XprType> EIGEN_DEVICE_REF type;
34};
35
36} // end namespace internal
37
43template <typename Strides, typename XprType>
44class TensorStridingOp : public TensorBase<TensorStridingOp<Strides, XprType> > {
45 public:
47 typedef typename Eigen::internal::traits<TensorStridingOp>::Scalar Scalar;
48 typedef typename Eigen::NumTraits<Scalar>::Real RealScalar;
49 typedef typename XprType::CoeffReturnType CoeffReturnType;
50 typedef typename Eigen::internal::ref_selector<TensorStridingOp>::type Nested;
51 typedef typename Eigen::internal::traits<TensorStridingOp>::StorageKind StorageKind;
52 typedef typename Eigen::internal::traits<TensorStridingOp>::Index Index;
53
54 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorStridingOp(const XprType& expr, const Strides& dims)
55 : m_xpr(expr), m_dims(dims) {}
56
57 EIGEN_DEVICE_FUNC const Strides& strides() const { return m_dims; }
58
59 EIGEN_DEVICE_FUNC const internal::remove_all_t<typename XprType::Nested>& expression() const { return m_xpr; }
60
61 EIGEN_INHERIT_ASSIGNMENT_OPERATORS(TensorStridingOp)
62
63 protected:
64 typename XprType::Nested m_xpr;
65 const Strides m_dims;
66};
67
68// Eval as rvalue
69template <typename Strides, typename ArgType, typename Device>
70struct TensorEvaluator<const TensorStridingOp<Strides, ArgType>, Device> {
72 typedef typename XprType::Index Index;
73 static constexpr int NumDims = internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value;
74 typedef DSizes<Index, NumDims> Dimensions;
75 typedef typename XprType::Scalar Scalar;
76 typedef typename XprType::CoeffReturnType CoeffReturnType;
77 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
78 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
79 typedef StorageMemory<CoeffReturnType, Device> Storage;
80 typedef typename Storage::Type EvaluatorPointerType;
81
82 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
83 // Inner-most dimension in storage order: the one a packet runs along.
84 static constexpr int kInnerDim = (static_cast<int>(Layout) == static_cast<int>(ColMajor)) ? 0 : NumDims - 1;
85 // Tag selecting whether the argument can be asked for whole packets.
86 typedef std::integral_constant<bool, TensorEvaluator<ArgType, Device>::PacketAccess> ImplHasPacket;
87 enum {
88 IsAligned = false,
89 // Packets are assembled from inner runs even when the nested evaluator
90 // only exposes coefficient access.
91 PacketAccess = (PacketSize > 1),
92 BlockAccess = false,
94 CoordAccess = false, // to be implemented
95 RawAccess = false
96 };
97
98 //===- Tensor block evaluation strategy (see TensorBlock.h) -------------===//
99 typedef internal::TensorBlockNotImplemented TensorBlock;
100 //===--------------------------------------------------------------------===//
101
102 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device) : m_impl(op.expression(), device) {
103 m_dimensions = m_impl.dimensions();
104 m_is_identity = true;
105 for (int i = 0; i < NumDims; ++i) {
106 m_dimensions[i] = Eigen::numext::ceil(static_cast<float>(m_dimensions[i]) / op.strides()[i]);
107 if (op.strides()[i] != 1) m_is_identity = false;
108 }
109
110 const typename TensorEvaluator<ArgType, Device>::Dimensions& input_dims = m_impl.dimensions();
111 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
112 m_outputStrides[0] = 1;
113 m_inputStrides[0] = 1;
114 for (int i = 1; i < NumDims; ++i) {
115 m_outputStrides[i] = m_outputStrides[i - 1] * m_dimensions[i - 1];
116 m_inputStrides[i] = m_inputStrides[i - 1] * input_dims[i - 1];
117 m_inputStrides[i - 1] *= op.strides()[i - 1];
118 }
119 m_inputStrides[NumDims - 1] *= op.strides()[NumDims - 1];
120 } else { // RowMajor
121 m_outputStrides[NumDims - 1] = 1;
122 m_inputStrides[NumDims - 1] = 1;
123 for (int i = NumDims - 2; i >= 0; --i) {
124 m_outputStrides[i] = m_outputStrides[i + 1] * m_dimensions[i + 1];
125 m_inputStrides[i] = m_inputStrides[i + 1] * input_dims[i + 1];
126 m_inputStrides[i + 1] *= op.strides()[i + 1];
127 }
128 m_inputStrides[0] *= op.strides()[0];
129 }
130 }
131
132 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Dimensions& dimensions() const { return m_dimensions; }
133
134 EIGEN_STRONG_INLINE bool evalSubExprsIfNeeded(EvaluatorPointerType /*data*/) {
135 m_impl.evalSubExprsIfNeeded(nullptr);
136 return true;
137 }
138 EIGEN_STRONG_INLINE void cleanup() { m_impl.cleanup(); }
139
140 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE CoeffReturnType coeff(Index index) const {
141 if (m_is_identity) {
142 return m_impl.coeff(index);
143 }
144 return m_impl.coeff(srcCoeff(index));
145 }
146
147 // Reads PacketSize coefficients of the argument starting at `base` and
148 // stepping by `inner_stride`. Callers guarantee those lanes lie in a single
149 // inner-most run, which is what makes the input indices an arithmetic
150 // progression and lets the index mapping be computed once per packet
151 // instead of once per coefficient. The argument's packet() must not be
152 // instantiated when it has no packet access, hence the tag-dispatched pair
153 // rather than a plain branch (C++14 has no if constexpr).
154 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType loadInnerRun(Index base, Index inner_stride,
155 std::false_type) const {
156 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
157 std::remove_const_t<CoeffReturnType> values[PacketSize];
158 EIGEN_UNROLL_LOOP
159 for (int i = 0; i < PacketSize; ++i) {
160 values[i] = m_impl.coeff(base + i * inner_stride);
161 }
162 return internal::pload<PacketReturnType>(values);
163 }
164
165 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType loadInnerRun(Index base, Index inner_stride,
166 std::true_type) const {
167 if (inner_stride == 1) return m_impl.template packet<Unaligned>(base);
168 return loadInnerRun(base, inner_stride, std::false_type());
169 }
170
171 template <int LoadMode>
172 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketReturnType packet(Index index) const {
173 eigen_assert(index + PacketSize - 1 < dimensions().TotalSize());
174 // Identity striding is just the inner-run case with step one: srcCoeff is
175 // then the identity and the inner input stride is 1.
176 if (m_is_identity) return loadInnerRun(index, 1, ImplHasPacket());
177 Index base;
178 if (packetStaysInInnerRun(index, base)) return loadInnerRun(base, m_inputStrides[kInnerDim], ImplHasPacket());
179
180 // The packet crosses an inner-run boundary, so every lane needs its own
181 // index mapping.
182 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment)
183 std::remove_const_t<CoeffReturnType> values[PacketSize];
184 EIGEN_UNROLL_LOOP
185 for (int i = 0; i < PacketSize; ++i) {
186 values[i] = coeff(index + i);
187 }
188 return internal::pload<PacketReturnType>(values);
189 }
190
191 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE TensorOpCost costPerCoeff(bool vectorized) const {
192 const double compute_cost = m_is_identity
193 ? TensorOpCost::AddCost<Index>()
194 : (NumDims - 1) * (TensorOpCost::AddCost<Index>() + TensorOpCost::MulCost<Index>() +
195 TensorOpCost::DivCost<Index>()) +
196 TensorOpCost::MulCost<Index>();
197 // The nested evaluator is served whole packets only when it has packet
198 // access at all, and the inner runs are packet-aligned with an unstrided
199 // inner dimension; everywhere else it is driven coefficient by coefficient
200 // and must be charged at scalar rates. Likewise the once-per-packet index
201 // mapping amortizes only while packets stay inside one inner run; the
202 // cross-run fallback recomputes it per lane. Identity striding satisfies
203 // both conditions, so it needs no special case here.
204 const bool packets_stay_in_inner =
205 m_is_identity || (m_dimensions[kInnerDim] > 0 && m_dimensions[kInnerDim] % PacketSize == 0);
206 const bool packetizes_arg =
207 TensorEvaluator<ArgType, Device>::PacketAccess && packets_stay_in_inner && m_inputStrides[kInnerDim] == 1;
208 return m_impl.costPerCoeff(vectorized && packetizes_arg) +
209 TensorOpCost(0, 0, compute_cost, vectorized && packets_stay_in_inner, PacketSize);
210 }
211
212 EIGEN_DEVICE_FUNC typename Storage::Type data() const { return nullptr; }
213
214 protected:
215 // Computes the input index of output index `index` and, as a by-product of
216 // the same walk, the output's inner-dimension coordinate. The packet paths
217 // use the latter to test whether a whole packet stays inside one inner-most
218 // run without spending an extra division on it.
219 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index srcCoeffInner(Index index, Index& inner_pos) const {
220 Index inputIndex = 0;
221 EIGEN_IF_CONSTEXPR (static_cast<int>(Layout) == static_cast<int>(ColMajor)) {
222 EIGEN_UNROLL_LOOP
223 for (int i = NumDims - 1; i > 0; --i) {
224 const Index idx = index / m_outputStrides[i];
225 inputIndex += idx * m_inputStrides[i];
226 index -= idx * m_outputStrides[i];
227 }
228 inner_pos = index;
229 inputIndex += index * m_inputStrides[0];
230 } else { // RowMajor
231 EIGEN_UNROLL_LOOP
232 for (int i = 0; i < NumDims - 1; ++i) {
233 const Index idx = index / m_outputStrides[i];
234 inputIndex += idx * m_inputStrides[i];
235 index -= idx * m_outputStrides[i];
236 }
237 inner_pos = index;
238 inputIndex += index * m_inputStrides[NumDims - 1];
239 }
240 return inputIndex;
241 }
242
243 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Index srcCoeff(Index index) const {
244 Index inner_pos;
245 return srcCoeffInner(index, inner_pos);
246 }
247
248 // True when the whole packet at output `index` stays inside one inner-most
249 // run; sets `base` to the input index of its first lane either way.
250 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE bool packetStaysInInnerRun(Index index, Index& base) const {
251 Index inner_pos;
252 base = srcCoeffInner(index, inner_pos);
253 return inner_pos + PacketSize <= m_dimensions[kInnerDim];
254 }
255
256 Dimensions m_dimensions;
257 bool m_is_identity;
258 array<Index, NumDims> m_outputStrides;
259 array<Index, NumDims> m_inputStrides;
260 TensorEvaluator<ArgType, Device> m_impl;
261};
262
263// Eval as lvalue
264template <typename Strides, typename ArgType, typename Device>
265struct TensorEvaluator<TensorStridingOp<Strides, ArgType>, Device>
266 : public TensorEvaluator<const TensorStridingOp<Strides, ArgType>, Device> {
267 typedef TensorStridingOp<Strides, ArgType> XprType;
268 typedef TensorEvaluator<const XprType, Device> Base;
269 static constexpr int NumDims = internal::array_size<typename TensorEvaluator<ArgType, Device>::Dimensions>::value;
270
271 typedef typename XprType::Index Index;
272 typedef typename XprType::Scalar Scalar;
273 typedef typename XprType::CoeffReturnType CoeffReturnType;
274 typedef typename PacketType<CoeffReturnType, Device>::type PacketReturnType;
275 static constexpr int PacketSize = PacketType<CoeffReturnType, Device>::size;
276 static constexpr int Layout = TensorEvaluator<ArgType, Device>::Layout;
277 enum {
278 IsAligned = false,
279 // Packets are scattered into inner runs even when the nested evaluator
280 // only exposes coefficient access.
281 PacketAccess = Base::PacketAccess,
282 PreferBlockAccess = false,
283 CoordAccess = false, // to be implemented
284 RawAccess = false
285 };
286
287 EIGEN_STRONG_INLINE TensorEvaluator(const XprType& op, const Device& device) : Base(op, device) {}
288
289 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar& coeffRef(Index index) const {
290 if (this->m_is_identity) return this->m_impl.coeffRef(index);
291 return this->m_impl.coeffRef(this->srcCoeff(index));
292 }
293
294 // Mirror of the rvalue loadInnerRun: scatters a packet across PacketSize
295 // coefficients starting at `base` and stepping by `inner_stride`.
296 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void storeInnerRun(Index base, Index inner_stride, const PacketReturnType& x,
297 std::false_type) const {
298 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment) Scalar values[PacketSize];
299 internal::pstore<Scalar, PacketReturnType>(values, x);
300 EIGEN_UNROLL_LOOP
301 for (int i = 0; i < PacketSize; ++i) {
302 this->m_impl.coeffRef(base + i * inner_stride) = values[i];
303 }
304 }
305
306 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void storeInnerRun(Index base, Index inner_stride, const PacketReturnType& x,
307 std::true_type) const {
308 if (inner_stride == 1) {
309 this->m_impl.template writePacket<Unaligned>(base, x);
310 return;
311 }
312 storeInnerRun(base, inner_stride, x, std::false_type());
313 }
314
315 template <int StoreMode>
316 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void writePacket(Index index, const PacketReturnType& x) const {
317 eigen_assert(index + PacketSize - 1 < this->dimensions().TotalSize());
318 typedef typename Base::ImplHasPacket ImplHasPacket;
319 if (this->m_is_identity) {
320 storeInnerRun(index, 1, x, ImplHasPacket());
321 return;
322 }
323 Index base;
324 if (this->packetStaysInInnerRun(index, base)) {
325 storeInnerRun(base, this->m_inputStrides[Base::kInnerDim], x, ImplHasPacket());
326 return;
327 }
328
329 // The packet crosses an inner-run boundary, so every lane needs its own
330 // index mapping.
331 EIGEN_ALIGN_TO_BOUNDARY(internal::unpacket_traits<PacketReturnType>::alignment) Scalar values[PacketSize];
332 internal::pstore<Scalar, PacketReturnType>(values, x);
333 EIGEN_UNROLL_LOOP
334 for (int i = 0; i < PacketSize; ++i) {
335 this->coeffRef(index + i) = values[i];
336 }
337 }
338};
339
340} // end namespace Eigen
341
342#endif // EIGEN_TENSOR_TENSOR_STRIDING_H
The tensor base class.
Definition TensorForwardDeclarations.h:69
Tensor striding class.
Definition TensorStriding.h:44
Namespace containing all symbols from the Eigen library.
The tensor evaluator class.
Definition TensorEvaluator.h:47