10#ifndef EIGEN_CXX11_TENSOR_TENSOR_EXECUTOR_H
11#define EIGEN_CXX11_TENSOR_TENSOR_EXECUTOR_H
14#include "./InternalHeaderCheck.h"
30template <
typename Expression>
32 enum { value =
false };
35template <
typename LhsXprType,
typename RhsXprType>
37 enum { value = ExpressionHasTensorBroadcastingOp<RhsXprType>::value };
40template <
typename UnaryOp,
typename XprType>
41struct ExpressionHasTensorBroadcastingOp<const TensorCwiseUnaryOp<UnaryOp, XprType> > {
42 enum { value = ExpressionHasTensorBroadcastingOp<XprType>::value };
45template <
typename BinaryOp,
typename LhsXprType,
typename RhsXprType>
48 value = ExpressionHasTensorBroadcastingOp<LhsXprType>::value || ExpressionHasTensorBroadcastingOp<RhsXprType>::value
52template <
typename Broadcast,
typename XprType>
54 enum { value =
true };
75template <
typename Expression,
typename Device,
bool Vectorizable, TiledEvaluation Tiling>
78 typedef typename Expression::Index StorageIndex;
85 static_assert(std::is_same<Device, DefaultDevice>::value,
86 "Default executor instantiated with non-default device. "
87 "You must #define EIGEN_USE_THREADS, EIGEN_USE_GPU or "
88 "EIGEN_USE_SYCL before including Eigen headers.");
90 static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
const Device& device = DefaultDevice()) {
92 const bool needs_assign = evaluator.evalSubExprsIfNeeded(NULL);
94 const StorageIndex size = array_prod(evaluator.dimensions());
95 for (StorageIndex i = 0; i < size; ++i) {
96 evaluator.evalScalar(i);
107template <
typename Expression,
typename Device,
typename DoneCallback,
bool Vectorizable, TiledEvaluation Tiling>
113template <
typename Expression>
115 TiledEvaluation::Off> {
117 typedef typename Expression::Index StorageIndex;
119 static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
const DefaultDevice& device = DefaultDevice()) {
121 const bool needs_assign = evaluator.evalSubExprsIfNeeded(NULL);
123 const StorageIndex size = array_prod(evaluator.dimensions());
124 const int PacketSize =
125 unpacket_traits<typename TensorEvaluator<Expression, DefaultDevice>::PacketReturnType>::size;
130 const StorageIndex UnrolledSize = (size / (4 * PacketSize)) * 4 * PacketSize;
131 for (StorageIndex i = 0; i < UnrolledSize; i += 4 * PacketSize) {
132 for (StorageIndex j = 0; j < 4; j++) {
133 evaluator.evalPacket(i + j * PacketSize);
136 const StorageIndex VectorizedSize = (size / PacketSize) * PacketSize;
137 for (StorageIndex i = UnrolledSize; i < VectorizedSize; i += PacketSize) {
138 evaluator.evalPacket(i);
140 for (StorageIndex i = VectorizedSize; i < size; ++i) {
141 evaluator.evalScalar(i);
152template <
typename Expression,
bool Vectorizable>
154 TiledEvaluation::On> {
156 typedef typename traits<Expression>::Scalar Scalar;
157 typedef std::remove_const_t<Scalar> ScalarNoConst;
160 typedef typename traits<Expression>::Index StorageIndex;
162 static constexpr int NumDims = traits<Expression>::NumDimensions;
164 EIGEN_DEVICE_FUNC
static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
165 const DefaultDevice& device = DefaultDevice()) {
166 typedef TensorBlockMapper<NumDims, Evaluator::Layout, StorageIndex> TensorBlockMapper;
168 typedef internal::TensorBlockDescriptor<NumDims, StorageIndex> TensorBlockDesc;
169 typedef internal::TensorBlockScratchAllocator<DefaultDevice> TensorBlockScratch;
171 Evaluator evaluator(expr, device);
174 const bool needs_assign = evaluator.evalSubExprsIfNeeded(NULL);
178 const TensorBlockResourceRequirements requirements = evaluator.getResourceRequirements();
180 const TensorBlockMapper block_mapper(
typename TensorBlockDesc::Dimensions(evaluator.dimensions()), requirements);
183 TensorBlockScratch scratch(device);
185 const StorageIndex total_block_count = block_mapper.blockCount();
186 for (StorageIndex i = 0; i < total_block_count; ++i) {
187 TensorBlockDesc desc = block_mapper.blockDescriptor(i);
188 evaluator.evalBlock(desc, scratch);
207#ifdef EIGEN_USE_THREADS
209template <
typename TensorBlockMapper>
210struct TensorExecutorTilingContext {
211 TensorExecutorTilingContext() =
default;
212 TensorExecutorTilingContext(
const TensorBlockMapper& b_mapper,
const TensorOpCost& b_cost,
size_t b_aligned_size)
213 : block_mapper(b_mapper), cost(b_cost), aligned_blocksize(b_aligned_size) {}
215 TensorBlockMapper block_mapper;
217 size_t aligned_blocksize;
222template <
typename Evaluator,
typename TensorBlockMapper,
bool Vectorizable>
223TensorExecutorTilingContext<TensorBlockMapper> GetTensorExecutorTilingContext(
const Evaluator& evaluator) {
225 TensorBlockResourceRequirements requirements = evaluator.getResourceRequirements();
228 double taskSize = TensorCostModel<ThreadPoolDevice>::taskSize(1, requirements.cost_per_coeff);
229 requirements.size =
static_cast<size_t>(1.0 / taskSize);
231 TensorBlockMapper block_mapper(
typename TensorBlockMapper::Dimensions(evaluator.dimensions()), requirements);
233 size_t block_size = block_mapper.blockTotalSize();
234 const size_t align = numext::maxi(EIGEN_MAX_ALIGN_BYTES, 1);
235 const size_t aligned_blocksize =
236 align * numext::div_ceil<size_t>(block_size *
sizeof(
typename Evaluator::Scalar), align);
238 return {block_mapper, requirements.cost_per_coeff * block_size, aligned_blocksize};
241template <
typename Evaluator,
typename StorageIndex,
bool Vectorizable>
243 static void run(Evaluator* evaluator_in,
const StorageIndex firstIdx,
const StorageIndex lastIdx) {
244 Evaluator evaluator = *evaluator_in;
245 eigen_assert(lastIdx >= firstIdx);
246 for (StorageIndex i = firstIdx; i < lastIdx; ++i) {
247 evaluator.evalScalar(i);
251 static StorageIndex alignBlockSize(StorageIndex size) {
return size; }
254template <
typename Evaluator,
typename StorageIndex>
255struct EvalRange<Evaluator, StorageIndex, true> {
256 static constexpr int PacketSize = unpacket_traits<typename Evaluator::PacketReturnType>::size;
258 static void run(Evaluator* evaluator_in,
const StorageIndex firstIdx,
const StorageIndex lastIdx) {
259 Evaluator evaluator = *evaluator_in;
260 eigen_assert(lastIdx >= firstIdx);
261 StorageIndex i = firstIdx;
262 if (lastIdx - firstIdx >= PacketSize) {
263 eigen_assert(firstIdx % PacketSize == 0);
264 StorageIndex last_chunk_offset = lastIdx - 4 * PacketSize;
268 for (; i <= last_chunk_offset; i += 4 * PacketSize) {
269 for (StorageIndex j = 0; j < 4; j++) {
270 evaluator.evalPacket(i + j * PacketSize);
273 last_chunk_offset = lastIdx - PacketSize;
274 for (; i <= last_chunk_offset; i += PacketSize) {
275 evaluator.evalPacket(i);
278 for (; i < lastIdx; ++i) {
279 evaluator.evalScalar(i);
283 static StorageIndex alignBlockSize(StorageIndex size) {
285 if (size >= 16 * PacketSize) {
286 return (size + 4 * PacketSize - 1) & ~(4 * PacketSize - 1);
289 return (size + PacketSize - 1) & ~(PacketSize - 1);
293template <
typename Expression,
bool Vectorizable, TiledEvaluation Tiling>
294class TensorExecutor<Expression, ThreadPoolDevice, Vectorizable, Tiling> {
296 typedef typename Expression::Index StorageIndex;
298 static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
const ThreadPoolDevice& device) {
299 typedef TensorEvaluator<Expression, ThreadPoolDevice> Evaluator;
300 typedef EvalRange<Evaluator, StorageIndex, Vectorizable> EvalRange;
302 Evaluator evaluator(expr, device);
303 const bool needs_assign = evaluator.evalSubExprsIfNeeded(
nullptr);
305 const StorageIndex size = array_prod(evaluator.dimensions());
307 size, evaluator.costPerCoeff(Vectorizable), EvalRange::alignBlockSize,
308 [&evaluator](StorageIndex firstIdx, StorageIndex lastIdx) { EvalRange::run(&evaluator, firstIdx, lastIdx); });
314template <
typename Expression,
bool Vectorizable>
316 TiledEvaluation::On> {
318 typedef typename traits<Expression>::Index IndexType;
319 typedef typename traits<Expression>::Scalar Scalar;
320 typedef std::remove_const_t<Scalar> ScalarNoConst;
322 static constexpr int NumDims = traits<Expression>::NumDimensions;
324 typedef TensorEvaluator<Expression, ThreadPoolDevice> Evaluator;
325 typedef TensorBlockMapper<NumDims, Evaluator::Layout, IndexType> BlockMapper;
326 typedef TensorExecutorTilingContext<BlockMapper> TilingContext;
328 typedef internal::TensorBlockDescriptor<NumDims, IndexType> TensorBlockDesc;
329 typedef internal::TensorBlockScratchAllocator<ThreadPoolDevice> TensorBlockScratch;
331 static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
const ThreadPoolDevice& device) {
332 Evaluator evaluator(expr, device);
334 const bool needs_assign = evaluator.evalSubExprsIfNeeded(
nullptr);
336 const TilingContext tiling =
337 internal::GetTensorExecutorTilingContext<Evaluator, BlockMapper, Vectorizable>(evaluator);
339 auto eval_block = [&device, &evaluator, &tiling](IndexType firstBlockIdx, IndexType lastBlockIdx) {
340 TensorBlockScratch scratch(device);
342 for (IndexType block_idx = firstBlockIdx; block_idx < lastBlockIdx; ++block_idx) {
343 TensorBlockDesc desc = tiling.block_mapper.blockDescriptor(block_idx);
344 evaluator.evalBlock(desc, scratch);
350 if (tiling.block_mapper.blockCount() == 1) {
351 TensorBlockScratch scratch(device);
352 TensorBlockDesc desc(0, tiling.block_mapper.blockDimensions());
353 evaluator.evalBlock(desc, scratch);
355 device.parallelFor(tiling.block_mapper.blockCount(), tiling.cost, std::move(eval_block));
362template <
typename Expression,
typename DoneCallback,
bool Vectorizable, TiledEvaluation Tiling>
363class TensorAsyncExecutor<Expression, ThreadPoolDevice, DoneCallback, Vectorizable, Tiling> {
365 typedef typename Expression::Index StorageIndex;
366 typedef TensorEvaluator<Expression, ThreadPoolDevice> Evaluator;
368 static EIGEN_STRONG_INLINE
void runAsync(
const Expression& expr,
const ThreadPoolDevice& device, DoneCallback done) {
369 TensorAsyncExecutorContext*
const ctx =
new TensorAsyncExecutorContext(expr, device, std::move(done));
371 const auto on_eval_subexprs = [ctx, &device](
bool need_assign) ->
void {
377 typedef EvalRange<Evaluator, StorageIndex, Vectorizable> EvalRange;
378 const StorageIndex size = array_prod(ctx->evaluator.dimensions());
379 device.parallelForAsync(
380 size, ctx->evaluator.costPerCoeff(Vectorizable), EvalRange::alignBlockSize,
381 [ctx](StorageIndex firstIdx, StorageIndex lastIdx) { EvalRange::run(&ctx->evaluator, firstIdx, lastIdx); },
382 [ctx]() { delete ctx; });
385 ctx->evaluator.evalSubExprsIfNeededAsync(
nullptr, on_eval_subexprs);
389 struct TensorAsyncExecutorContext {
390 TensorAsyncExecutorContext(
const Expression& expr,
const ThreadPoolDevice& thread_pool, DoneCallback done)
391 : evaluator(expr, thread_pool), on_done(std::move(done)) {}
393 ~TensorAsyncExecutorContext() {
401 DoneCallback on_done;
405template <
typename Expression,
typename DoneCallback,
bool Vectorizable>
406class TensorAsyncExecutor<Expression, ThreadPoolDevice, DoneCallback, Vectorizable, TiledEvaluation::On> {
408 typedef typename traits<Expression>::Index IndexType;
409 typedef typename traits<Expression>::Scalar Scalar;
410 typedef std::remove_const_t<Scalar> ScalarNoConst;
412 static constexpr int NumDims = traits<Expression>::NumDimensions;
414 typedef TensorEvaluator<Expression, ThreadPoolDevice> Evaluator;
415 typedef TensorBlockMapper<NumDims, Evaluator::Layout, IndexType> BlockMapper;
416 typedef TensorExecutorTilingContext<BlockMapper> TilingContext;
418 typedef internal::TensorBlockDescriptor<NumDims, IndexType> TensorBlockDesc;
419 typedef internal::TensorBlockScratchAllocator<ThreadPoolDevice> TensorBlockScratch;
421 static EIGEN_STRONG_INLINE
void runAsync(
const Expression& expr,
const ThreadPoolDevice& device, DoneCallback done) {
422 TensorAsyncExecutorContext*
const ctx =
new TensorAsyncExecutorContext(expr, device, std::move(done));
424 const auto on_eval_subexprs = [ctx](
bool need_assign) ->
void {
430 ctx->tiling = internal::GetTensorExecutorTilingContext<Evaluator, BlockMapper, Vectorizable>(ctx->evaluator);
432 auto eval_block = [ctx](IndexType firstBlockIdx, IndexType lastBlockIdx) {
433 TensorBlockScratch scratch(ctx->device);
435 for (IndexType block_idx = firstBlockIdx; block_idx < lastBlockIdx; ++block_idx) {
436 TensorBlockDesc desc = ctx->tiling.block_mapper.blockDescriptor(block_idx);
437 ctx->evaluator.evalBlock(desc, scratch);
443 if (ctx->tiling.block_mapper.blockCount() == 1) {
444 TensorBlockScratch scratch(ctx->device);
445 TensorBlockDesc desc(0, ctx->tiling.block_mapper.blockDimensions());
446 ctx->evaluator.evalBlock(desc, scratch);
449 ctx->device.parallelForAsync(ctx->tiling.block_mapper.blockCount(), ctx->tiling.cost, eval_block,
450 [ctx]() { delete ctx; });
454 ctx->evaluator.evalSubExprsIfNeededAsync(
nullptr, on_eval_subexprs);
458 struct TensorAsyncExecutorContext {
459 TensorAsyncExecutorContext(
const Expression& expr,
const ThreadPoolDevice& thread_pool, DoneCallback done)
460 : device(thread_pool), evaluator(expr, thread_pool), on_done(std::move(done)) {}
462 ~TensorAsyncExecutorContext() {
467 const ThreadPoolDevice& device;
469 TilingContext tiling;
472 DoneCallback on_done;
479#if defined(EIGEN_USE_GPU)
481template <
typename Expression,
bool Vectorizable, TiledEvaluation Tiling>
482class TensorExecutor<Expression, GpuDevice, Vectorizable, Tiling> {
484 typedef typename Expression::Index StorageIndex;
485 static void run(
const Expression& expr,
const GpuDevice& device);
488#if defined(EIGEN_GPUCC)
490template <
typename Index>
491EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
int sum_will_overflow(
Index lhs,
Index rhs) {
492 const Index highest = NumTraits<Index>::highest();
493 const Index lowest = NumTraits<Index>::lowest();
494 if (lhs > 0 && rhs > 0) {
495 return lhs > highest - rhs ? 1 : 0;
496 }
else if (lhs < 0 && rhs < 0) {
497 return lhs < lowest - rhs ? -1 : 0;
505template <
typename Index>
506EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
Index saturate_add(
Index lhs,
Index rhs) {
507 const Index highest = NumTraits<Index>::highest();
508 const Index lowest = NumTraits<Index>::lowest();
509 int overflow = sum_will_overflow(lhs, rhs);
510 return overflow == 1 ? highest : overflow == -1 ? lowest : lhs + rhs;
516template <
typename Index>
521 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE SafeStep(Index lastIdx, Index step_size)
522 : can_overflow_(sum_will_overflow(lastIdx, step_size)), step_size_(step_size) {}
525 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
Index operator()(Index index)
const {
526 return can_overflow_ ? saturate_add(index, step_size_) : index + step_size_;
530 const bool can_overflow_;
531 const Index step_size_;
534template <
typename Evaluator,
typename StorageIndex,
bool Vectorizable>
535struct EigenMetaKernelEval {
536 static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
void run(Evaluator& eval, StorageIndex firstIdx, StorageIndex lastIdx,
537 StorageIndex step_size) {
538 SafeStep<StorageIndex> safe_step(lastIdx, step_size);
539 for (StorageIndex i = firstIdx; i < lastIdx; i = safe_step(i)) {
545template <
typename Evaluator,
typename StorageIndex>
546struct EigenMetaKernelEval<Evaluator, StorageIndex, true> {
547 static EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
void run(Evaluator& eval, StorageIndex firstIdx, StorageIndex lastIdx,
548 StorageIndex step_size) {
549 const StorageIndex PacketSize = unpacket_traits<typename Evaluator::PacketReturnType>::size;
550 const StorageIndex vectorized_size = (lastIdx / PacketSize) * PacketSize;
551 const StorageIndex vectorized_step_size = step_size * PacketSize;
553 SafeStep<StorageIndex> safe_vectorized_step(vectorized_size, vectorized_step_size);
555 for (StorageIndex i = firstIdx * PacketSize; i < vectorized_size; i = safe_vectorized_step(i)) {
558 SafeStep<StorageIndex> safe_step(lastIdx, step_size);
559 for (StorageIndex i = saturate_add(vectorized_size, firstIdx); i < lastIdx; i = safe_step(i)) {
565template <
typename Evaluator,
typename StorageIndex>
566__global__
void __launch_bounds__(1024) EigenMetaKernel(Evaluator eval, StorageIndex size) {
567 const StorageIndex first_index = blockIdx.x * blockDim.x + threadIdx.x;
568 const StorageIndex step_size = blockDim.x * gridDim.x;
570 const bool vectorizable = Evaluator::PacketAccess & Evaluator::IsAligned;
571 EigenMetaKernelEval<Evaluator, StorageIndex, vectorizable>::run(eval, first_index, size, step_size);
575template <
typename Expression,
bool Vectorizable, TiledEvaluation Tiling>
576EIGEN_STRONG_INLINE
void TensorExecutor<Expression, GpuDevice, Vectorizable, Tiling>::run(
const Expression& expr,
577 const GpuDevice& device) {
578 TensorEvaluator<Expression, GpuDevice> evaluator(expr, device);
579 const bool needs_assign = evaluator.evalSubExprsIfNeeded(
nullptr);
581 const int block_size = device.maxGpuThreadsPerBlock();
582 const int max_blocks =
static_cast<int>(
583 numext::mini<int64_t>(device.getNumGpuMultiProcessors() * device.maxGpuThreadsPerMultiProcessor(),
584 NumTraits<StorageIndex>::highest()) /
586 const StorageIndex size = array_prod(evaluator.dimensions());
588 const int num_blocks = numext::maxi<int>(
589 numext::mini<int>(max_blocks,
static_cast<int>(numext::div_ceil<StorageIndex>(size, block_size))), 1);
591 LAUNCH_GPU_KERNEL((EigenMetaKernel<TensorEvaluator<Expression, GpuDevice>, StorageIndex>), num_blocks, block_size,
592 0, device, evaluator, size);
603template <
typename Evaluator>
604struct ExecExprFunctorKernel {
605 typedef typename Evaluator::Index
Index;
608 template <
typename Scratch>
609 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE ExecExprFunctorKernel(
const Scratch, Evaluator evaluator_,
const Index range_)
610 : evaluator(evaluator_), range(range_) {}
612 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE
void operator()(cl::sycl::nd_item<1> itemID)
const { compute(itemID); }
613 template <
bool is_vec = Evaluator::PacketAccess>
614 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE std::enable_if_t<!is_vec> compute(
const cl::sycl::nd_item<1>& itemID)
const {
615 Index gId =
static_cast<Index>(itemID.get_global_linear_id());
616 Index total_threads = itemID.get_global_range(0);
618 for (Index i = gId; i < range; i += total_threads) {
619 evaluator.evalScalar(i);
622 template <
bool is_vec = Evaluator::PacketAccess>
623 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE std::enable_if_t<is_vec> compute(
const cl::sycl::nd_item<1>& itemID)
const {
624 const Index vectorizedRange = (range / Evaluator::PacketSize) * Evaluator::PacketSize;
625 Index gId =
static_cast<Index>(itemID.get_global_linear_id());
626 const Index step = Evaluator::PacketSize * itemID.get_global_range(0);
627 const Index start = Evaluator::PacketSize * gId;
628 for (Index i = start; i < vectorizedRange; i += step) {
629 evaluator.evalPacket(i);
631 gId += vectorizedRange;
632 for (Index i = gId; i < range; i += itemID.get_global_range(0)) {
633 evaluator.evalScalar(i);
638template <
typename Expression,
bool Vectorizable, TiledEvaluation Tiling>
639class TensorExecutor<Expression, Eigen::SyclDevice, Vectorizable, Tiling> {
641 typedef typename Expression::Index
Index;
642 static EIGEN_STRONG_INLINE
void run(
const Expression& expr,
const Eigen::SyclDevice& dev) {
643 typedef Eigen::TensorEvaluator<Expression, Eigen::SyclDevice> Evaluator;
644 Evaluator evaluator(expr, dev);
645 const bool needs_assign = evaluator.evalSubExprsIfNeeded(NULL);
647 Index range, GRange, tileSize;
648 Index total_size = ::Eigen::internal::array_prod(evaluator.dimensions());
649 total_size = (total_size == 0) ? 1 : total_size;
650 const int PacketSize = Eigen::PacketType<typename Evaluator::CoeffReturnType, Eigen::SyclDevice>::size;
651 Index vectorizable_threads =
static_cast<Index>(total_size / PacketSize);
652 dev.parallel_for_setup(vectorizable_threads, tileSize, range, GRange);
655 dev.template nullary_kernel_launcher<typename Evaluator::CoeffReturnType, ExecExprFunctorKernel<Evaluator> >(
656 evaluator, cl::sycl::nd_range<1>(cl::sycl::range<1>(GRange), cl::sycl::range<1>(tileSize)),
Index(1),
Definition TensorAssign.h:55
Definition TensorExecutor.h:108
The tensor executor class.
Definition TensorExecutor.h:76
Namespace containing all symbols from the Eigen library.
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The tensor evaluator class.
Definition TensorEvaluator.h:30
Definition TensorExecutor.h:31