/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #pragma once #include #include "paddle/common/enforce.h" #include "paddle/phi/kernels/funcs/elementwise_base.h" #if defined(__NVCC__) || defined(__HIPCC__) || defined(__xpu__) #include "paddle/phi/kernels/funcs/dims_simplifier.h" #endif namespace phi { namespace funcs { #if defined(__NVCC__) || defined(__HIPCC__) || defined(__xpu__) enum BroadcastType { kMixed = 1, kBroadcast = 2, kElementwise = 3 }; template struct BroadcastTypeClassifier { int64_t numel{0}; int broadcast_num{0}; // Not used for XPU bool all_elementwise{true}; // Not used for XPU Array use_broadcast; // Not used for XPU Array configs; Array ins_data; Array<_ptr_ OutT *, NumOuts> outs_data; BroadcastTypeClassifier() {} BroadcastTypeClassifier(const std::vector &ins, std::vector *outs, int axis) { numel = (*outs)[0]->numel(); #ifndef PADDLE_WITH_XPU_KP for (size_t i = 0; i < ins.size(); ++i) { bool is_same_dim = ins[i]->numel() == numel; if (is_same_dim) { use_broadcast[i] = false; } else { use_broadcast[i] = true; broadcast_num++; } all_elementwise &= is_same_dim; } #endif InitBroadcastConfigs(ins, outs, axis); using Traits = funcs::FunctionTraits; using ArgsT = typename Traits::ArgsTuple; ArgsT arg; UnrollerWithoutVecSize::step(ins, arg, &ins_data); for (int i = 0; i < NumOuts; ++i) { outs_data[i] = (*outs)[i]->data(); } } void InitBroadcastConfigs(const std::vector &ins, std::vector *outs, int axis) { #ifdef PADDLE_WITH_XPU_KP const auto dims_simplifier = BroadcastDimsSimplifier(ins, (*outs)[0]->dims(), axis); if (VLOG_IS_ON(6)) { DimsSimplifiedLogger::Log( ins, outs, dims_simplifier, "BroadcastKernel"); } configs[0] = kps::details::BroadcastConfig(dims_simplifier.out_dims, dims_simplifier.in_dims[0], dims_simplifier.in_dims[1], dims_simplifier.rank); configs[1] = kps::details::BroadcastConfig(dims_simplifier.out_dims, dims_simplifier.in_dims[1], dims_simplifier.in_dims[0], dims_simplifier.rank); #else if (!all_elementwise) { const auto dims_simplifier = BroadcastDimsSimplifier(ins, (*outs)[0]->dims(), axis); if (VLOG_IS_ON(6)) { DimsSimplifiedLogger::Log( ins, outs, dims_simplifier, "BroadcastKernel"); } for (int i = 0; i < Arity; ++i) { // if data shape is[m, n], then you should set data_dim = {n, m} // eg: out's shape [3, 45, 1]. then out_dims = {1, 45, 3} // if (ins[i]->numel() != (*outs)[0]->numel()) { if (ins[i]->numel()) { configs[i] = kps::details::BroadcastConfig(dims_simplifier.out_dims, dims_simplifier.in_dims[i], dims_simplifier.rank); } } } #endif } }; // Common broadcast/elementwise Loader. template struct BroadcastDataLoader { template static __device__ __forceinline__ void Apply(const Array1 &ins, ArgsT *args, const Array2 &configs, const Array3 &use_broadcast, const int block_offset, const int num, const uint32_t numel, int read_lens) { using Type = std::tuple_element_t; #ifdef PADDLE_WITH_XPU_KP kps::Init( args, static_cast(1.0f), read_lens); if (use_broadcast[Index]) { kps::ReadDataBc( args, reinterpret_cast(ins[Index]), block_offset, configs[Index], numel, read_lens); } else { kps::ReadData( args, reinterpret_cast(ins[Index]) + block_offset, num, read_lens); } #else kps::Init(args, static_cast(1.0f)); if (use_broadcast[Index]) { kps::ReadDataBc( args, reinterpret_cast(ins[Index]), block_offset, configs[Index], numel, VecSize); } // NOTE: If use if...else... with condition `use_broadcast[Index]` here, // there will be some errs with clang12 while compiling in ROCm. // When the compiler is upgraded, if...else... may be used. if (!use_broadcast[Index]) { kps::ReadData( args, reinterpret_cast(ins[Index]) + block_offset, num, VecSize); } #endif } }; /* BroadcastDataLoaders Partial specialization */ #ifndef PADDLE_WITH_XPU_KP // Scalar elementwise Loader with consideration of IsBoundary. template struct BroadcastDataLoader { template static __device__ __forceinline__ void Apply(const Array1 &ins, ArgsT *args, const Array2 &configs, const Array3 &use_broadcast, const int block_offset, const int num, const uint32_t numel, int read_lens) { using Type = std::tuple_element_t; int thread_offset = threadIdx.x * VecSize + block_offset; #pragma unroll for (int idx = 0; idx < VecSize; ++idx) { std::get(args[idx]) = static_cast(1); int index = thread_offset + idx; if (index < numel) { std::get(args[idx]) = reinterpret_cast(ins[Index])[index]; } } } }; // Vectorized elementwise Loader without consideration of IsBoundary. template struct BroadcastDataLoader { template static __device__ __forceinline__ void Apply(const Array1 &ins, ArgsT *args, const Array2 &configs, const Array3 &use_broadcast, const int block_offset, const int num, const uint32_t numel, int read_lens) { using Type = std::tuple_element_t; using VecType = phi::kps::details::VectorType; VecType vec_temp; int64_t thread_offset = static_cast(threadIdx.x) + static_cast(blockIdx.x) * static_cast(blockDim.x); const VecType *__restrict__ vec_input = reinterpret_cast(ins[Index]); vec_temp = vec_input[thread_offset]; #pragma unroll for (int idx = 0; idx < VecSize; ++idx) { std::get(args[idx]) = vec_temp.val[idx]; } } }; template struct BroadcastDataInit { template static __device__ __forceinline__ void Apply(ArgsT *args) { using Type = std::tuple_element_t; #pragma unroll for (int k = 0; k < VecSize; ++k) { std::get(args[k]) = static_cast(1); } } }; template struct BroadcastDataSetter { template static __device__ __forceinline__ void Apply(const Array &ins, ArgsT *args, uint32_t index_bc[][VecSize]) { using Type = std::tuple_element_t; #pragma unroll for (int k = 0; k < VecSize; ++k) { std::get(args[k]) = reinterpret_cast(ins[Index])[index_bc[Index][k]]; } } }; #endif // static broadcast unroller template