Files
paddlepaddle--paddle/paddle/phi/kernels/funcs/fast_divmod.h
T
2026-07-13 12:40:42 +08:00

128 lines
3.7 KiB
C++

/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.1 (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.1
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 <cstdint>
#include "paddle/phi/kernels/funcs/aligned_vector.h"
#define INT_BITS 32
#if defined(__xpu__)
#define __forceinline__ __inline__
#endif
namespace phi {
namespace funcs {
/**
* Fast division : Replace division in CUDA with multiplication to improve
* kernel performance.
* 1. Complete the division calculation on the CPU, and record the calculation
* results by using the divider and shift_val.
* 2. Set the divisor on the GPU through Div() to complete the calculation.
*/
template <typename IndexT>
struct FastDivMod {
// 1st value represents the result of input number divides by recorded divisor
// 2nd value represents the result of input number modulo by recorded divisor
using DivModT = AlignedVector<uint32_t, 2>;
FastDivMod() {}
HOSTDEVICE FastDivMod(uint32_t d) : divisor(d) {
static_assert(sizeof(unsigned int) == 4,
"Only Support 32-bit unsigned int.");
for (shift_val = 0; shift_val < INT_BITS; ++shift_val) {
auto shift_limit = 1 << shift_val;
if (shift_limit >= divisor) break;
}
uint64_t long_one = 1;
uint64_t temp_div =
((long_one << INT_BITS) * ((long_one << shift_val) - divisor)) /
divisor +
1;
multiplier = temp_div;
}
__device__ __forceinline__ uint32_t Div(uint32_t n) const {
uint32_t t = __umulhi(n, multiplier);
return (t + n) >> shift_val;
}
__device__ __forceinline__ DivModT Divmod(uint32_t n) const {
uint32_t q = Div(n);
DivModT result = {q, n - q * divisor};
return result;
}
__device__ __forceinline__ uint32_t DivCeil(uint32_t n) const {
DivModT res = Divmod(n);
return res.val[1] > 0 ? res.val[0] + 1 : res.val[0];
}
int32_t shift_val;
uint32_t divisor;
uint32_t multiplier;
};
template <>
struct FastDivMod<int64_t> {
using DivModT = AlignedVector<uint64_t, 2>;
FastDivMod() {}
HOSTDEVICE FastDivMod(uint64_t d) : divisor(d) {
for (shift_val = 0; shift_val < 64; ++shift_val) {
uint64_t shift_limit = uint64_t(1) << shift_val;
if (shift_limit >= divisor) break;
}
// quotient = ((uint128_t)n_hi << 64) / d
uint64_t quotient = 0;
uint64_t n_hi = (uint64_t(1) << shift_val) - d, n_lo = 0;
for (int i = 63; i >= 0; --i) {
uint64_t d_hi = i == 0 ? 0 : d >> (64 - i);
uint64_t d_lo = d << i;
if (n_hi == 0 && n_lo == 0) break;
if ((d_hi < n_hi) || (d_hi <= n_hi && d_lo <= n_lo)) {
quotient |= uint64_t(1) << i;
n_hi -= d_hi + (d_lo > n_lo);
n_lo -= d_lo;
}
}
multiplier = quotient + 1;
}
__device__ __forceinline__ uint64_t Div(uint64_t n) const {
uint64_t t = __umul64hi(n, multiplier);
return (t + n) >> shift_val;
}
__device__ __forceinline__ DivModT Divmod(uint64_t n) const {
uint64_t q = Div(n);
return {q, n - q * divisor};
}
__device__ __forceinline__ uint64_t DivCeil(uint64_t n) const {
DivModT res = Divmod(n);
return res.val[1] > 0 ? res.val[0] + 1 : res.val[0];
}
int shift_val;
uint64_t divisor;
uint64_t multiplier;
};
} // namespace funcs
} // namespace phi