// Copyright (c) 2023 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. /* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. */ /*This code is copied from NVIDIA apex: * https://github.com/NVIDIA/apex * with minor changes. */ #pragma once #include "ln_bwd_kernels.h" // NOLINT #include "ln_fwd_kernels.h" // NOLINT #include "ln_utils.h" // NOLINT namespace phi { namespace layer_norm { template struct KernelTraitsBase { using weight_t = weight_t_; using input_t = input_t_; using output_t = output_t_; using compute_t = compute_t_; using index_t = index_t_; enum { HIDDEN_SIZE = HIDDEN_SIZE_ }; enum { THREADS_PER_CTA = THREADS_PER_CTA_ }; enum { THREADS_PER_WARP = 32 }; }; template > struct KernelTraitsFinalize : public Base { enum { ROWS_PER_CTA = Base::THREADS_PER_CTA / Base::THREADS_PER_WARP }; static_assert((int)ROWS_PER_CTA <= (int)Base::THREADS_PER_WARP); // NOLINT // Bytes per global load from the input. enum { BYTES_PER_LDG = BYTES_PER_LDG_ }; // Number of elements fetched by a global load. enum { ELTS_PER_LDG = BYTES_PER_LDG / sizeof(compute_t_) }; // Bytes per global store of the weights. enum { BYTES_PER_STG = ELTS_PER_LDG * sizeof(weight_t_) }; static_assert( sizeof(BYTES_PER_LDG) == 4, "Conflict-free smem transpose only implemented for 4B compute type!"); static_assert(Base::THREADS_PER_CTA == ROWS_PER_CTA * Base::THREADS_PER_WARP, "We assume one warp per row!"); // The total number of BYTES_PER_LDG-wide words in a hidden vector. enum { COLS = HIDDEN_SIZE_ * sizeof(compute_t_) / BYTES_PER_LDG }; static_assert(COLS * BYTES_PER_LDG == HIDDEN_SIZE_ * sizeof(compute_t_)); // Shared memory size to transpose the CTA result. enum { SMEM_BYTES_TRANSPOSE = Base::THREADS_PER_CTA * BYTES_PER_LDG }; // Shared memory size to coalesce the CTA result. enum { SMEM_BYTES_OUTPUT = Base::THREADS_PER_WARP * BYTES_PER_LDG }; // Shared memory requirement per CTA. enum { SMEM_BYTES_PER_CTA = 2 * SMEM_BYTES_TRANSPOSE + 2 * SMEM_BYTES_OUTPUT }; // The type of the reducer. using Reducer = layer_norm::Reducer; // Condition for the whole CTA to participate in syncthreads. static_assert(COLS % Base::THREADS_PER_WARP == 0); enum { CTAS = COLS / Base::THREADS_PER_WARP }; }; //////////////////////////////////////////////////////////////////////////////////////////////////// template > struct KernelTraits : public Base { using input_t = typename Base::input_t; using weight_t = typename Base::weight_t; using compute_t = typename Base::compute_t; using output_t = typename Base::output_t; using index_t = typename Base::index_t; enum { CTAS_PER_ROW = CTAS_PER_ROW_ }; enum { WARPS_M = WARPS_M_ }; enum { WARPS_N = WARPS_N_ }; enum { COLS = HIDDEN_SIZE_ }; enum { HIDDEN_SIZE = HIDDEN_SIZE_ }; enum { BYTES_PER_LDG = BYTES_PER_LDG_ }; enum { NUM_ELTS = BYTES_PER_LDG / sizeof(input_t) }; enum { THREADS_PER_ROW = WARPS_N * THREADS_PER_WARP }; enum { THREADS_PER_CTA = WARPS_M * THREADS_PER_ROW }; enum { ROWS_PER_CTA = WARPS_M }; enum { BYTES_PER_ROW = COLS * sizeof(input_t) }; enum { BYTES_PER_ROW_PER_CTA = THREADS_PER_ROW * BYTES_PER_LDG }; // Multi-row per CTA not supported for multi-CTA => no smem for WGRAD needed enum { SMEM_BYTES_WGRAD = CTAS_PER_ROW > 1 ? 0 : ROWS_PER_CTA* COLS * sizeof(compute_t) }; static_assert(WARPS_M == 1 || CTAS_PER_ROW == 1); using reduce_t = typename layer_norm::TypeToVec2::Type; using Reducer = layer_norm::Reducer; enum { SMEM_BYTES_DGRAD = Reducer::SMEM_BYTES }; enum { SMEM_BYTES = SMEM_BYTES_DGRAD + SMEM_BYTES_WGRAD }; using Ivec = layer_norm::Vec; using Ovec = layer_norm::Vec; using Wvec = layer_norm::Vec; using Cvec = layer_norm::Vec; enum { ELTS_PER_LDG = BYTES_PER_LDG / sizeof(input_t) }; // Assume that each thread can handle the same number of elements in the // output and weights as in the input. static_assert(sizeof(input_t) >= sizeof(output_t)); static_assert(sizeof(input_t) >= sizeof(weight_t)); // The number of columns fetched per load from input: one per thread. enum { VEC_COLS_PER_LDG = CTAS_PER_ROW * THREADS_PER_ROW }; // The total number of vectorized loads/stores per hidden vector. enum { VEC_COLS = COLS / ELTS_PER_LDG }; // The number of loads per thread for the input. enum { LDGS = VEC_COLS / VEC_COLS_PER_LDG }; static_assert(LDGS * VEC_COLS_PER_LDG == VEC_COLS); // static_assert(LDGS * BYTES_PER_ROW_PER_CTA * CTAS_PER_ROW == BYTES_PER_ROW, // ""); using Stats = layer_norm::Stats; enum { SMEM_BYTES_FWD = Stats::SMEM_BYTES }; }; } // namespace layer_norm } // namespace phi