mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
178 lines
9.6 KiB
C++
178 lines
9.6 KiB
C++
// This file is part of Eigen, a lightweight C++ template library
|
|
// for linear algebra.
|
|
//
|
|
// Mehdi Goli Codeplay Software Ltd.
|
|
// Ralph Potter Codeplay Software Ltd.
|
|
// Luke Iwanski Codeplay Software Ltd.
|
|
// Contact: <eigen@codeplay.com>
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla
|
|
// Public License v. 2.0. If a copy of the MPL was not distributed
|
|
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
/*****************************************************************
|
|
* TensorSyclPlaceHolderExpr.h
|
|
*
|
|
* \brief:
|
|
* This is the specialisation of the placeholder expression based on the
|
|
* operation type
|
|
*
|
|
*****************************************************************/
|
|
|
|
#ifndef UNSUPPORTED_EIGEN_CXX11_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
|
|
#define UNSUPPORTED_EIGEN_CXX11_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
|
|
|
|
namespace Eigen {
|
|
namespace internal {
|
|
|
|
template<typename OP, typename CoeffReturnType> struct syclGenericBufferReducer{
|
|
template<typename BufferTOut, typename BufferTIn>
|
|
static void run(OP op, BufferTOut& bufOut, ptrdiff_t out_offset, BufferTIn& bufI, const Eigen::SyclDevice& dev, size_t length, size_t local){
|
|
do {
|
|
auto f = [length, local, op, out_offset, &bufOut, &bufI](cl::sycl::handler& h) mutable {
|
|
cl::sycl::nd_range<1> r{cl::sycl::range<1>{std::max(length, local)},
|
|
cl::sycl::range<1>{std::min(length, local)}};
|
|
/* Two accessors are used: one to the buffer that is being reduced,
|
|
* and a second to local memory, used to store intermediate data. */
|
|
auto aI =bufI.template get_access<cl::sycl::access::mode::read_write>(h);
|
|
auto aOut =bufOut.template get_access<cl::sycl::access::mode::write>(h);
|
|
typedef decltype(aI) InputAccessor;
|
|
typedef decltype(aOut) OutputAccessor;
|
|
typedef cl::sycl::accessor<CoeffReturnType, 1, cl::sycl::access::mode::read_write,cl::sycl::access::target::local> LocalAccessor;
|
|
LocalAccessor scratch(cl::sycl::range<1>(local), h);
|
|
|
|
/* The parallel_for invocation chosen is the variant with an nd_item
|
|
* parameter, since the code requires barriers for correctness. */
|
|
h.parallel_for(r, TensorSycl::internal::GenericKernelReducer<CoeffReturnType, OP, OutputAccessor, InputAccessor, LocalAccessor>(op, aOut, out_offset, aI, scratch, length, local));
|
|
};
|
|
dev.sycl_queue().submit(f);
|
|
dev.asynchronousExec();
|
|
|
|
/* At this point, you could queue::wait_and_throw() to ensure that
|
|
* errors are caught quickly. However, this would likely impact
|
|
* performance negatively. */
|
|
length = length / local;
|
|
|
|
} while (length > 1);
|
|
}
|
|
|
|
};
|
|
|
|
template<typename CoeffReturnType> struct syclGenericBufferReducer<Eigen::internal::MeanReducer<CoeffReturnType>, CoeffReturnType>{
|
|
template<typename BufferTOut, typename BufferTIn>
|
|
static void run(Eigen::internal::MeanReducer<CoeffReturnType>, BufferTOut& bufOut,ptrdiff_t out_offset, BufferTIn& bufI, const Eigen::SyclDevice& dev, size_t length, size_t local){
|
|
syclGenericBufferReducer<Eigen::internal::SumReducer<CoeffReturnType>, CoeffReturnType>::run(Eigen::internal::SumReducer<CoeffReturnType>(),
|
|
bufOut, out_offset, bufI, dev, length, local);
|
|
}
|
|
};
|
|
|
|
/// Self is useless here because in expression construction we are going to treat reduction as a leafnode.
|
|
/// we want to take reduction child and then build a construction and apply the full reducer function on it. Fullreducre applies the
|
|
/// reduction operation on the child of the reduction. once it is done the reduction is an empty shell and can be thrown away and treated as
|
|
// a leafNode.
|
|
|
|
template <typename Self, typename Op, bool Vectorizable>
|
|
struct FullReducer<Self, Op, const Eigen::SyclDevice, Vectorizable> {
|
|
|
|
typedef typename Self::CoeffReturnType CoeffReturnType;
|
|
static const bool HasOptimizedImplementation = false;
|
|
|
|
static void run(const Self& self, Op& reducer, const Eigen::SyclDevice& dev, CoeffReturnType* output) {
|
|
typedef const typename Self::ChildType HostExpr; /// this is the child of reduction
|
|
typedef Eigen::TensorSycl::internal::FunctorExtractor<TensorEvaluator<HostExpr, const Eigen::SyclDevice> > FunctorExpr;
|
|
FunctorExpr functors = TensorSycl::internal::extractFunctors(self.impl());
|
|
int red_factor =256; /// initial reduction. If the size is less than red_factor we only creates one thread.
|
|
size_t inputSize =self.impl().dimensions().TotalSize();
|
|
size_t rng = inputSize/red_factor; // the total number of thread initially is half the size of the input
|
|
size_t remaining = inputSize% red_factor;
|
|
if(rng ==0) {
|
|
red_factor=1;
|
|
};
|
|
size_t tileSize =dev.sycl_queue().get_device(). template get_info<cl::sycl::info::device::max_work_group_size>()/2;
|
|
size_t GRange=std::max((size_t )1, rng);
|
|
|
|
// convert global range to power of 2 for redecution
|
|
GRange--;
|
|
GRange |= GRange >> 1;
|
|
GRange |= GRange >> 2;
|
|
GRange |= GRange >> 4;
|
|
GRange |= GRange >> 8;
|
|
GRange |= GRange >> 16;
|
|
#if __x86_64__ || __ppc64__ || _WIN64
|
|
GRange |= GRange >> 32;
|
|
#endif
|
|
GRange++;
|
|
size_t outTileSize = tileSize;
|
|
/// if the shared memory is less than the GRange, we set shared_mem size to the TotalSize and in this case one kernel would be created for recursion to reduce all to one.
|
|
if (GRange < outTileSize) outTileSize=GRange;
|
|
/// creating the shared memory for calculating reduction.
|
|
/// This one is used to collect all the reduced value of shared memory as we don't have global barrier on GPU. Once it is saved we can
|
|
/// recursively apply reduction on it in order to reduce the whole.
|
|
auto temp_global_buffer =cl::sycl::buffer<CoeffReturnType, 1>(cl::sycl::range<1>(GRange));
|
|
typedef typename Eigen::internal::remove_all<decltype(self.xprDims())>::type Dims;
|
|
// Dims dims= self.xprDims();
|
|
//Op functor = reducer;
|
|
dev.sycl_queue().submit([&](cl::sycl::handler &cgh) {
|
|
// this is a workaround for gcc 4.8 bug
|
|
typedef decltype(TensorSycl::internal::createTupleOfAccessors(cgh, self.impl())) TupleType;
|
|
// create a tuple of accessors from Evaluator
|
|
TupleType tuple_of_accessors = TensorSycl::internal::createTupleOfAccessors(cgh, self.impl());
|
|
auto tmp_global_accessor = temp_global_buffer. template get_access<cl::sycl::access::mode::read_write, cl::sycl::access::target::global_buffer>(cgh);
|
|
typedef decltype(tmp_global_accessor) OutAccessor;
|
|
cgh.parallel_for( cl::sycl::nd_range<1>(cl::sycl::range<1>(GRange), cl::sycl::range<1>(outTileSize)),
|
|
TensorSycl::internal::FullReductionKernelFunctor<CoeffReturnType, OutAccessor, HostExpr, FunctorExpr, Op, Dims, size_t, TupleType>
|
|
(tmp_global_accessor, rng, remaining, red_factor, reducer, self.xprDims(), functors, tuple_of_accessors));
|
|
});
|
|
dev.asynchronousExec();
|
|
|
|
// getting final out buffer at the moment the created buffer is true because there is no need for assign
|
|
auto out_buffer =dev.get_sycl_buffer(output);
|
|
ptrdiff_t out_offset = dev.get_offset(output);
|
|
/// This is used to recursively reduce the tmp value to an element of 1;
|
|
syclGenericBufferReducer<Op, CoeffReturnType>::run(reducer, out_buffer, out_offset, temp_global_buffer,dev, GRange, outTileSize);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
template <typename Self, typename Op>
|
|
struct InnerReducer<Self, Op, const Eigen::SyclDevice> {
|
|
|
|
typedef typename Self::CoeffReturnType CoeffReturnType;
|
|
static const bool HasOptimizedImplementation = false;
|
|
|
|
static bool run(const Self& self, Op& reducer, const Eigen::SyclDevice& dev, CoeffReturnType* output, typename Self::Index num_values_to_reduce, typename Self::Index num_coeffs_to_preserve) {
|
|
typedef const typename Self::ChildType HostExpr; /// this is the child of reduction
|
|
typedef Eigen::TensorSycl::internal::FunctorExtractor<TensorEvaluator<HostExpr, const Eigen::SyclDevice> > FunctorExpr;
|
|
FunctorExpr functors = TensorSycl::internal::extractFunctors(self.impl());
|
|
typename Self::Index range, GRange, tileSize;
|
|
typedef typename Eigen::internal::remove_all<decltype(self.xprDims())>::type Dims;
|
|
|
|
// getting final out buffer at the moment the created buffer is true because there is no need for assign
|
|
/// creating the shared memory for calculating reduction.
|
|
/// This one is used to collect all the reduced value of shared memory as we don't have global barrier on GPU. Once it is saved we can
|
|
/// recursively apply reduction on it in order to reduce the whole.
|
|
dev.parallel_for_setup(num_coeffs_to_preserve, tileSize, range, GRange);
|
|
dev.sycl_queue().submit([&](cl::sycl::handler &cgh) {
|
|
// this is workaround for gcc 4.8 bug.
|
|
typedef decltype(TensorSycl::internal::createTupleOfAccessors(cgh, self.impl())) Tuple_of_Acc;
|
|
// create a tuple of accessors from Evaluator
|
|
Tuple_of_Acc tuple_of_accessors = TensorSycl::internal::createTupleOfAccessors(cgh, self.impl());
|
|
auto output_accessor = dev.template get_sycl_accessor<cl::sycl::access::mode::write>(cgh, output);
|
|
ptrdiff_t out_offset = dev.get_offset(output);
|
|
Index red_size = (num_values_to_reduce!=0)? num_values_to_reduce : static_cast<Index>(1);
|
|
cgh.parallel_for( cl::sycl::nd_range<1>(cl::sycl::range<1>(GRange), cl::sycl::range<1>(tileSize)),
|
|
TensorSycl::internal::ReductionFunctor<HostExpr, FunctorExpr, Tuple_of_Acc, Dims, Op, typename Self::Index>
|
|
(output_accessor, out_offset, functors, tuple_of_accessors, self.xprDims(), reducer, range, red_size));
|
|
|
|
});
|
|
dev.asynchronousExec();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
} // end namespace internal
|
|
} // namespace Eigen
|
|
|
|
#endif // UNSUPPORTED_EIGEN_CXX11_SRC_TENSOR_TENSOR_REDUCTION_SYCL_HPP
|