Eigen  5.0.1
 
Loading...
Searching...
No Matches
PartialReduxEvaluator.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2011-2018 Gael Guennebaud <gael.guennebaud@inria.fr>
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_PARTIALREDUX_H
12#define EIGEN_PARTIALREDUX_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21/***************************************************************************
22 *
23 * This file provides evaluators for partial reductions.
24 * There are two modes:
25 *
26 * - scalar path: simply calls the respective function on the column or row.
27 * -> nothing special here, all the tricky part is handled by the return
28 * types of VectorwiseOp's members. They embed the functor calling the
29 * respective DenseBase's member function.
30 *
31 * - vectorized path: implements a packet-wise reductions followed by
32 * some (optional) processing of the outcome, e.g., division by n for mean.
33 *
34 * For the vectorized path let's observe that the packet-size and outer-unrolling
35 * are both decided by the assignment logic. So all we have to do is to decide
36 * on the inner unrolling.
37 *
38 * For the unrolling, we can reuse "internal::redux_vec_unroller" from Redux.h,
39 * but we need to be careful to specify correct increment.
40 *
41 ***************************************************************************/
42
43/* logic deciding a strategy for unrolling of vectorized paths */
44template <typename Func, typename Evaluator>
45struct packetwise_redux_traits {
46 static constexpr int OuterSize =
47 int(Evaluator::IsRowMajor) ? Evaluator::RowsAtCompileTime : Evaluator::ColsAtCompileTime;
48 static constexpr int Cost = OuterSize == Dynamic
49 ? HugeCost
50 : OuterSize * Evaluator::CoeffReadCost + (OuterSize - 1) * functor_traits<Func>::Cost;
51 static constexpr int Unrolling = Cost <= EIGEN_UNROLLING_LIMIT ? CompleteUnrolling : NoUnrolling;
52};
53
54/* Value to be returned when size==0 , by default let's return 0 */
55template <typename PacketType, typename Func>
56EIGEN_DEVICE_FUNC PacketType packetwise_redux_empty_value(const Func&) {
57 const typename unpacket_traits<PacketType>::type zero(0);
58 return pset1<PacketType>(zero);
59}
60
61/* For products the default is 1 */
62template <typename PacketType, typename Scalar>
63EIGEN_DEVICE_FUNC PacketType packetwise_redux_empty_value(const scalar_product_op<Scalar, Scalar>&) {
64 return pset1<PacketType>(Scalar(1));
65}
66
67/* Perform the actual reduction */
68template <typename Func, typename Evaluator, int Unrolling = packetwise_redux_traits<Func, Evaluator>::Unrolling>
69struct packetwise_redux_impl;
70
71/* Perform the actual reduction with unrolling */
72template <typename Func, typename Evaluator>
73struct packetwise_redux_impl<Func, Evaluator, CompleteUnrolling> {
74 using Base = redux_novec_unroller<Func, Evaluator, 0, Evaluator::SizeAtCompileTime>;
75 using Scalar = typename Evaluator::Scalar;
76
77 template <typename PacketType>
78 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE PacketType run(const Evaluator& eval, const Func& func, Index /*size*/) {
79 return redux_vec_unroller<Func, Evaluator, 0,
80 packetwise_redux_traits<Func, Evaluator>::OuterSize>::template run<PacketType>(eval,
81 func);
82 }
83};
84
85/* Add a specialization of redux_vec_unroller for size==0 at compiletime.
86 * This specialization is not required for general reductions, which is
87 * why it is defined here.
88 */
89template <typename Func, typename Evaluator, Index Start>
90struct redux_vec_unroller<Func, Evaluator, Start, 0> {
91 template <typename PacketType>
92 EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE PacketType run(const Evaluator&, const Func& f) {
93 return packetwise_redux_empty_value<PacketType>(f);
94 }
95};
96
97/* Perform the actual reduction for dynamic sizes */
98template <typename Func, typename Evaluator>
99struct packetwise_redux_impl<Func, Evaluator, NoUnrolling> {
100 using Scalar = typename Evaluator::Scalar;
101 using PacketScalar = typename redux_traits<Func, Evaluator>::PacketType;
102
103 template <typename PacketType>
104 EIGEN_DEVICE_FUNC static PacketType run(const Evaluator& eval, const Func& func, Index size) {
105 if (size == 0) return packetwise_redux_empty_value<PacketType>(func);
106
107 const Index size4 = 1 + numext::round_down(size - 1, 4);
108 PacketType p = eval.template packetByOuterInner<Unaligned, PacketType>(0, 0);
109 // This loop is optimized for instruction pipelining:
110 // - each iteration generates two independent instructions
111 // - thanks to branch prediction and out-of-order execution we have independent instructions across loops
112 for (Index i = 1; i < size4; i += 4)
113 p = func.packetOp(
114 p, func.packetOp(func.packetOp(eval.template packetByOuterInner<Unaligned, PacketType>(i + 0, 0),
115 eval.template packetByOuterInner<Unaligned, PacketType>(i + 1, 0)),
116 func.packetOp(eval.template packetByOuterInner<Unaligned, PacketType>(i + 2, 0),
117 eval.template packetByOuterInner<Unaligned, PacketType>(i + 3, 0))));
118 for (Index i = size4; i < size; ++i)
119 p = func.packetOp(p, eval.template packetByOuterInner<Unaligned, PacketType>(i, 0));
120 return p;
121 }
122};
123
124template <typename Func, typename Evaluator>
125struct packetwise_segment_redux_impl {
126 using Scalar = typename Evaluator::Scalar;
127 using PacketScalar = typename redux_traits<Func, Evaluator>::PacketType;
128
129 template <typename PacketType>
130 EIGEN_DEVICE_FUNC static PacketType run(const Evaluator& eval, const Func& func, Index size, Index begin,
131 Index count) {
132 if (size == 0) return packetwise_redux_empty_value<PacketType>(func);
133
134 const Index size4 = 1 + numext::round_down(size - 1, 4);
135 PacketType p = eval.template packetSegmentByOuterInner<Unaligned, PacketType>(0, 0, begin, count);
136 // Grouping exposes independent packet ops and shortens the dependency chain.
137 for (Index i = 1; i < size4; i += 4)
138 p = func.packetOp(
139 p,
140 func.packetOp(
141 func.packetOp(eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i + 0, 0, begin, count),
142 eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i + 1, 0, begin, count)),
143 func.packetOp(eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i + 2, 0, begin, count),
144 eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i + 3, 0, begin, count))));
145 for (Index i = size4; i < size; ++i)
146 p = func.packetOp(p, eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i, 0, begin, count));
147 return p;
148 }
149};
150
151template <typename ArgType, typename MemberOp, int Direction>
152struct evaluator<PartialReduxExpr<ArgType, MemberOp, Direction> >
153 : evaluator_base<PartialReduxExpr<ArgType, MemberOp, Direction> > {
154 using XprType = PartialReduxExpr<ArgType, MemberOp, Direction>;
155 using ArgTypeNested = typename internal::nested_eval<ArgType, 1>::type;
156 using ConstArgTypeNested = add_const_on_value_type_t<ArgTypeNested>;
157 using ArgTypeNestedCleaned = internal::remove_all_t<ArgTypeNested>;
158 using InputScalar = typename ArgType::Scalar;
159 using Scalar = typename XprType::Scalar;
160 enum {
161 TraversalSize = Direction == int(Vertical) ? int(ArgType::RowsAtCompileTime) : int(ArgType::ColsAtCompileTime)
162 };
163 using CostOpType = typename MemberOp::template Cost<int(TraversalSize)>;
164 enum {
165 CoeffReadCost = TraversalSize == Dynamic ? HugeCost
166 : TraversalSize == 0
167 ? 1
168 : int(TraversalSize) * int(evaluator<ArgType>::CoeffReadCost) + int(CostOpType::value),
169
170 ArgFlags_ = evaluator<ArgType>::Flags,
171
172 Vectorizable_ = bool(int(ArgFlags_) & PacketAccessBit) && bool(MemberOp::Vectorizable) &&
173 (Direction == int(Vertical) ? bool(ArgFlags_ & RowMajorBit) : (ArgFlags_ & RowMajorBit) == 0) &&
174 (TraversalSize != 0),
175
176 Flags = (traits<XprType>::Flags & RowMajorBit) | (evaluator<ArgType>::Flags & (HereditaryBits & (~RowMajorBit))) |
177 (Vectorizable_ ? PacketAccessBit : 0) | LinearAccessBit,
178
179 Alignment = 0 // FIXME this will need to be improved once PartialReduxExpr is vectorized
180 };
181
182 EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) : m_arg(xpr.nestedExpression()), m_functor(xpr.functor()) {
183 EIGEN_INTERNAL_CHECK_COST_VALUE(TraversalSize == Dynamic ? HugeCost
184 : (TraversalSize == 0 ? 1 : int(CostOpType::value)));
185 EIGEN_INTERNAL_CHECK_COST_VALUE(CoeffReadCost);
186 }
187
188 using CoeffReturnType = typename XprType::CoeffReturnType;
189
190 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar coeff(Index i, Index j) const {
191 return coeff(Direction == Vertical ? j : i);
192 }
193
194 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar coeff(Index index) const {
195 return m_functor(m_arg.template subVector<DirectionType(Direction)>(index));
196 }
197
198 template <int LoadMode, typename PacketType>
199 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packet(Index i, Index j) const {
200 return packet<LoadMode, PacketType>(Direction == Vertical ? j : i);
201 }
202
203 template <int LoadMode, typename PacketType>
204 EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC PacketType packet(Index idx) const {
205 static constexpr int PacketSize = internal::unpacket_traits<PacketType>::size;
206 static constexpr int PanelRows = Direction == Vertical ? ArgType::RowsAtCompileTime : PacketSize;
207 static constexpr int PanelCols = Direction == Vertical ? PacketSize : ArgType::ColsAtCompileTime;
208 using PanelType = Block<const ArgTypeNestedCleaned, PanelRows, PanelCols, true /* InnerPanel */>;
209 using PanelEvaluator = typename internal::redux_evaluator<PanelType>;
210 using BinaryOp = typename MemberOp::BinaryOp;
211 using Impl = internal::packetwise_redux_impl<BinaryOp, PanelEvaluator>;
212
213 // Workaround for issue 1612 (closed): when PacketSize==1 (i.e. complex<double> with 128bits registers) the
214 // storage-order of panel gets reversed and methods like packetByOuterInner do not make sense in this context, so
215 // bypass "vectorization":
216 EIGEN_IF_CONSTEXPR (PacketSize == 1) return internal::pset1<PacketType>(coeff(idx));
217
218 Index startRow = Direction == Vertical ? 0 : idx;
219 Index startCol = Direction == Vertical ? idx : 0;
220 Index numRows = Direction == Vertical ? m_arg.rows() : PacketSize;
221 Index numCols = Direction == Vertical ? PacketSize : m_arg.cols();
222
223 PanelType panel(m_arg, startRow, startCol, numRows, numCols);
224 PanelEvaluator panel_eval(panel);
225 PacketType p = Impl::template run<PacketType>(panel_eval, m_functor.binaryFunc(), m_arg.outerSize());
226 return p;
227 }
228
229 template <int LoadMode, typename PacketType>
230 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packetSegment(Index i, Index j, Index begin, Index count) const {
231 return packetSegment<LoadMode, PacketType>(Direction == Vertical ? j : i, begin, count);
232 }
233
234 template <int LoadMode, typename PacketType>
235 EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC PacketType packetSegment(Index idx, Index begin, Index count) const {
236 static constexpr int PanelRows = Direction == Vertical ? ArgType::RowsAtCompileTime : Dynamic;
237 static constexpr int PanelCols = Direction == Vertical ? Dynamic : ArgType::ColsAtCompileTime;
238 using PanelType = Block<const ArgTypeNestedCleaned, PanelRows, PanelCols, true /* InnerPanel */>;
239 using PanelEvaluator = typename internal::redux_evaluator<PanelType>;
240 using BinaryOp = typename MemberOp::BinaryOp;
241 using Impl = internal::packetwise_segment_redux_impl<BinaryOp, PanelEvaluator>;
242
243 Index startRow = Direction == Vertical ? 0 : idx;
244 Index startCol = Direction == Vertical ? idx : 0;
245 Index numRows = Direction == Vertical ? m_arg.rows() : begin + count;
246 Index numCols = Direction == Vertical ? begin + count : m_arg.cols();
247
248 PanelType panel(m_arg, startRow, startCol, numRows, numCols);
249 PanelEvaluator panel_eval(panel);
250 PacketType p = Impl::template run<PacketType>(panel_eval, m_functor.binaryFunc(), m_arg.outerSize(), begin, count);
251 return p;
252 }
253
254 protected:
255 ConstArgTypeNested m_arg;
256 const MemberOp m_functor;
257};
258
259} // end namespace internal
260
261} // end namespace Eigen
262
263#endif // EIGEN_PARTIALREDUX_H
DirectionType
Definition Constants.h:264
@ Vertical
Definition Constants.h:267
constexpr unsigned int PacketAccessBit
Definition Constants.h:98
constexpr unsigned int LinearAccessBit
Definition Constants.h:134
constexpr unsigned int RowMajorBit
Definition Constants.h:71