Eigen  5.0.1
 
Loading...
Searching...
No Matches
Assign_AOCL.h
1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 *
6 * Assign_AOCL.h - AOCL Vectorized Math Dispatch Layer for Eigen
7 *
8 * Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
9 *
10 * Description:
11 * ------------
12 * This file implements a high-performance dispatch layer that automatically
13 * routes Eigen's element-wise mathematical operations to AMD Optimizing CPU
14 * Libraries (AOCL) Vector Math Library (VML) functions when beneficial for
15 * performance.
16 *
17 * The dispatch system uses C++ template specialization to intercept Eigen's
18 * assignment operations and redirect them to AOCL's VRDA functions, which
19 * provide optimized implementations for AMD Zen architectures.
20 *
21 * Key Features:
22 * -------------
23 * 1. Automatic Dispatch: Seamlessly routes supported operations to AOCL without
24 * requiring code changes in user applications
25 *
26 * 2. Performance Optimization: Uses AOCL VRDA functions optimized for Zen
27 * family processors with automatic SIMD instruction selection (AVX2, AVX-512)
28 *
29 * 3. Threshold-Based Activation: Only activates for vectors larger than
30 * EIGEN_AOCL_VML_THRESHOLD (default: 128 elements) to avoid overhead on
31 * small vectors
32 *
33 * 4. Precision-Specific Handling:
34 * - Double precision: AOCL VRDA vectorized functions
35 * - Single precision: Scalar fallback (preserves correctness)
36 *
37 * 5. Memory Layout Compatibility: Ensures direct memory access and compatible
38 * storage orders between source and destination for optimal performance
39 *
40 * Supported Operations:
41 * ---------------------
42 * UNARY OPERATIONS (vector → vector):
43 * - exp(), exp2(), sin(), cos(), sqrt(), cbrt(), abs(), log(), log10(), log2()
44 *
45 * BINARY OPERATIONS (vector op vector → vector):
46 * - +, pow(), max(), min()
47 *
48 * Template Specialization Mechanism:
49 * -----------------------------------
50 * The system works by specializing Eigen's Assignment template for:
51 * 1. CwiseUnaryOp with scalar_*_op functors (unary operations)
52 * 2. CwiseBinaryOp with scalar_*_op functors (binary operations)
53 * 3. Dense2Dense assignment context with AOCL-compatible traits
54 *
55 * Dispatch conditions (all must be true):
56 * - Source and destination have DirectAccessBit (contiguous memory)
57 * - Compatible storage orders (both row-major or both column-major)
58 * - Vector size ≥ EIGEN_AOCL_VML_THRESHOLD or Dynamic size
59 * - Supported data type (currently double precision for VRDA)
60 *
61 * Integration Example:
62 * --------------------
63 * // Standard Eigen code - no changes required
64 * VectorXd x = VectorXd::Random(10000);
65 * VectorXd y = VectorXd::Random(10000);
66 * VectorXd result;
67 *
68 * // These operations are automatically dispatched to AOCL:
69 * result = x.array().exp(); // → amd_vrda_exp()
70 * result = x.array().sin(); // → amd_vrda_sin()
71 * result = x.array() + y.array(); // → amd_vrda_add()
72 * result = x.array().pow(y.array()); // → amd_vrda_pow()
73 *
74 * Configuration:
75 * --------------
76 * Required preprocessor definitions:
77 * - EIGEN_USE_AOCL_ALL or EIGEN_USE_AOCL_MT: Enable AOCL integration
78 * - EIGEN_USE_AOCL_VML: Enable Vector Math Library dispatch
79 *
80 * Compilation Requirements:
81 * -------------------------
82 * Include paths:
83 * - AOCL headers: -I${AOCL_ROOT}/include
84 * - Eigen headers: -I/path/to/eigen
85 *
86 * Link libraries:
87 * - AOCL MathLib: -lamdlibm
88 * - Standard math: -lm
89 *
90 * Compiler flags:
91 * - Optimization: -O3 (required for inlining)
92 * - Architecture: -march=znver5 or -march=native
93 * - Vectorization: -mfma -mavx512f (if supported)
94 *
95 * Platform Support:
96 * ------------------
97 * - Primary: Linux x86_64 with AMD Zen family processors
98 * - Compilers: GCC 8+, Clang 10+, AOCC (recommended)
99 * - AOCL Version: 4.0+ (with VRDA support)
100 *
101 * Error Handling:
102 * ---------------
103 * - Graceful fallback to scalar operations for unsupported configurations
104 * - Compile-time detection of AOCL availability
105 * - Runtime size and alignment validation with eigen_assert()
106 *
107 * Developer:
108 * ----------
109 * Name: Sharad Saurabh Bhaskar
110 * Email: shbhaska@amd.com
111 * Organization: Advanced Micro Devices, Inc.
112 */
113// SPDX-License-Identifier: MPL-2.0
114
115#ifndef EIGEN_ASSIGN_AOCL_H
116#define EIGEN_ASSIGN_AOCL_H
117
118namespace Eigen {
119namespace internal {
120
121// Traits for unary operations.
122template <typename Dst, typename Src>
123class aocl_assign_traits {
124 private:
125 enum {
126 DstHasDirectAccess = !!(Dst::Flags & DirectAccessBit),
127 SrcHasDirectAccess = !!(Src::Flags & DirectAccessBit),
128 StorageOrdersAgree = (int(Dst::IsRowMajor) == int(Src::IsRowMajor)),
129 InnerSize = Dst::IsVectorAtCompileTime ? int(Dst::SizeAtCompileTime)
130 : (Dst::Flags & RowMajorBit) ? int(Dst::ColsAtCompileTime)
131 : int(Dst::RowsAtCompileTime),
132 LargeEnough = (InnerSize == Dynamic) || (InnerSize >= EIGEN_AOCL_VML_THRESHOLD)
133 };
134
135 public:
136 enum {
137 EnableAoclVML = DstHasDirectAccess && SrcHasDirectAccess && StorageOrdersAgree && LargeEnough,
138 Traversal = LinearTraversal
139 };
140};
141
142// Traits for binary operations (e.g., add, pow).
143template <typename Dst, typename Lhs, typename Rhs>
144class aocl_assign_binary_traits {
145 private:
146 enum {
147 DstHasDirectAccess = !!(Dst::Flags & DirectAccessBit),
148 LhsHasDirectAccess = !!(Lhs::Flags & DirectAccessBit),
149 RhsHasDirectAccess = !!(Rhs::Flags & DirectAccessBit),
150 StorageOrdersAgree =
151 (int(Dst::IsRowMajor) == int(Lhs::IsRowMajor)) && (int(Dst::IsRowMajor) == int(Rhs::IsRowMajor)),
152 InnerSize = Dst::IsVectorAtCompileTime ? int(Dst::SizeAtCompileTime)
153 : (Dst::Flags & RowMajorBit) ? int(Dst::ColsAtCompileTime)
154 : int(Dst::RowsAtCompileTime),
155 LargeEnough = (InnerSize == Dynamic) || (InnerSize >= EIGEN_AOCL_VML_THRESHOLD)
156 };
157
158 public:
159 enum {
160 EnableAoclVML = DstHasDirectAccess && LhsHasDirectAccess && RhsHasDirectAccess && StorageOrdersAgree && LargeEnough
161 };
162};
163
164// Unary operation dispatch for float (scalar fallback).
165#define EIGEN_AOCL_VML_UNARY_CALL_FLOAT(EIGENOP) \
166 template <typename DstXprType, typename SrcXprNested> \
167 struct Assignment<DstXprType, CwiseUnaryOp<scalar_##EIGENOP##_op<float>, SrcXprNested>, assign_op<float, float>, \
168 Dense2Dense, std::enable_if_t<aocl_assign_traits<DstXprType, SrcXprNested>::EnableAoclVML>> { \
169 typedef CwiseUnaryOp<scalar_##EIGENOP##_op<float>, SrcXprNested> SrcXprType; \
170 static void run(DstXprType &dst, const SrcXprType &src, const assign_op<float, float> &) { \
171 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
172 Eigen::Index n = dst.size(); \
173 if (n <= 0) return; \
174 const float *input = reinterpret_cast<const float *>(src.nestedExpression().data()); \
175 float *output = reinterpret_cast<float *>(dst.data()); \
176 for (Eigen::Index i = 0; i < n; ++i) { \
177 output[i] = std::EIGENOP(input[i]); \
178 } \
179 } \
180 };
181
182// Unary operation dispatch for double (AOCL vectorized).
183#define EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(EIGENOP, AOCLOP) \
184 template <typename DstXprType, typename SrcXprNested> \
185 struct Assignment<DstXprType, CwiseUnaryOp<scalar_##EIGENOP##_op<double>, SrcXprNested>, assign_op<double, double>, \
186 Dense2Dense, std::enable_if_t<aocl_assign_traits<DstXprType, SrcXprNested>::EnableAoclVML>> { \
187 typedef CwiseUnaryOp<scalar_##EIGENOP##_op<double>, SrcXprNested> SrcXprType; \
188 static void run(DstXprType &dst, const SrcXprType &src, const assign_op<double, double> &) { \
189 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
190 Eigen::Index n = dst.size(); \
191 eigen_assert(n <= INT_MAX && "AOCL does not support arrays larger than INT_MAX"); \
192 if (n <= 0) return; \
193 const double *input = reinterpret_cast<const double *>(src.nestedExpression().data()); \
194 double *output = reinterpret_cast<double *>(dst.data()); \
195 int aocl_n = internal::convert_index<int>(n); \
196 AOCLOP(aocl_n, const_cast<double *>(input), output); \
197 } \
198 };
199
200// Instantiate unary calls for float (scalar).
201// EIGEN_AOCL_VML_UNARY_CALL_FLOAT(exp)
202
203// Instantiate unary calls for double (AOCL vectorized).
204EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(exp2, amd_vrda_exp2)
205EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(exp, amd_vrda_exp)
206EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(sin, amd_vrda_sin)
207EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(cos, amd_vrda_cos)
208EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(sqrt, amd_vrda_sqrt)
209EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(cbrt, amd_vrda_cbrt)
210EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(abs, amd_vrda_fabs)
211EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log, amd_vrda_log)
212EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log10, amd_vrda_log10)
213EIGEN_AOCL_VML_UNARY_CALL_DOUBLE(log2, amd_vrda_log2)
214
215// Binary operation dispatch for float (scalar fallback).
216#define EIGEN_AOCL_VML_BINARY_CALL_FLOAT(EIGENOP, STDFUNC) \
217 template <typename DstXprType, typename LhsXprNested, typename RhsXprNested> \
218 struct Assignment< \
219 DstXprType, CwiseBinaryOp<scalar_##EIGENOP##_op<float, float>, LhsXprNested, RhsXprNested>, \
220 assign_op<float, float>, Dense2Dense, \
221 std::enable_if_t<aocl_assign_binary_traits<DstXprType, LhsXprNested, RhsXprNested>::EnableAoclVML>> { \
222 typedef CwiseBinaryOp<scalar_##EIGENOP##_op<float, float>, LhsXprNested, RhsXprNested> SrcXprType; \
223 static void run(DstXprType &dst, const SrcXprType &src, const assign_op<float, float> &) { \
224 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
225 Eigen::Index n = dst.size(); \
226 if (n <= 0) return; \
227 const float *lhs = reinterpret_cast<const float *>(src.lhs().data()); \
228 const float *rhs = reinterpret_cast<const float *>(src.rhs().data()); \
229 float *output = reinterpret_cast<float *>(dst.data()); \
230 for (Eigen::Index i = 0; i < n; ++i) { \
231 output[i] = STDFUNC(lhs[i], rhs[i]); \
232 } \
233 } \
234 };
235
236// Binary operation dispatch for double (AOCL vectorized).
237#define EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(EIGENOP, AOCLOP) \
238 template <typename DstXprType, typename LhsXprNested, typename RhsXprNested> \
239 struct Assignment< \
240 DstXprType, CwiseBinaryOp<scalar_##EIGENOP##_op<double, double>, LhsXprNested, RhsXprNested>, \
241 assign_op<double, double>, Dense2Dense, \
242 std::enable_if_t<aocl_assign_binary_traits<DstXprType, LhsXprNested, RhsXprNested>::EnableAoclVML>> { \
243 typedef CwiseBinaryOp<scalar_##EIGENOP##_op<double, double>, LhsXprNested, RhsXprNested> SrcXprType; \
244 static void run(DstXprType &dst, const SrcXprType &src, const assign_op<double, double> &) { \
245 eigen_assert(dst.rows() == src.rows() && dst.cols() == src.cols()); \
246 Eigen::Index n = dst.size(); \
247 eigen_assert(n <= INT_MAX && "AOCL does not support arrays larger than INT_MAX"); \
248 if (n <= 0) return; \
249 const double *lhs = reinterpret_cast<const double *>(src.lhs().data()); \
250 const double *rhs = reinterpret_cast<const double *>(src.rhs().data()); \
251 double *output = reinterpret_cast<double *>(dst.data()); \
252 int aocl_n = internal::convert_index<int>(n); \
253 AOCLOP(aocl_n, const_cast<double *>(lhs), const_cast<double *>(rhs), output); \
254 } \
255 };
256
257// Instantiate binary calls for float (scalar).
258// EIGEN_AOCL_VML_BINARY_CALL_FLOAT(sum, std::plus<float>) // Using scalar_sum_op for addition
259// EIGEN_AOCL_VML_BINARY_CALL_FLOAT(pow, std::pow)
260
261// Instantiate binary calls for double (AOCL vectorized).
262EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(sum, amd_vrda_add) // Using scalar_sum_op for addition
263EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(pow, amd_vrda_pow)
264EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(max, amd_vrda_fmax)
265EIGEN_AOCL_VML_BINARY_CALL_DOUBLE(min, amd_vrda_fmin)
266
267} // namespace internal
268} // namespace Eigen
269
270#endif // EIGEN_ASSIGN_AOCL_H
constexpr unsigned int DirectAccessBit
Definition Constants.h:160
constexpr unsigned int RowMajorBit
Definition Constants.h:71