mirror of
https://github.com/fmtlib/fmt.git
synced 2026-07-30 08:16:31 +08:00
Optimize 128-bit integer formatting
count_digits and do_format_decimal divided the full 128-bit value by a constant in a loop. Compilers do not strength-reduce 128-bit division, so each iteration became a slow __udivti3 library call (~27 per number). Reduce the value to 64-bit chunks by dividing by 10^19 (at most twice) and reuse the fast 64-bit paths, giving a ~5x speedup on full-range values.
This commit is contained in:
parent
b5d1e5404b
commit
7b4ef1c814
@ -1064,12 +1064,6 @@ template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
#if FMT_USE_INT128
|
||||
FMT_CONSTEXPR inline auto count_digits(native_uint128 n) -> int {
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FMT_BUILTIN_CLZLL
|
||||
// It is a separate function rather than a part of count_digits to workaround
|
||||
// the lack of static constexpr in constexpr functions.
|
||||
@ -1100,6 +1094,20 @@ FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
|
||||
#if FMT_USE_INT128
|
||||
FMT_CONSTEXPR20 inline auto count_digits(native_uint128 n) -> int {
|
||||
if (is_constant_evaluated()) return count_digits_fallback(n);
|
||||
// 128-bit division is a slow library call, so reduce to 64-bit chunks via
|
||||
// 10^19 (the largest power of 10 fitting in uint64_t).
|
||||
const uint64_t pow10_19 = 10000000000000000000ULL;
|
||||
if ((n >> 64) == 0) return count_digits(static_cast<uint64_t>(n));
|
||||
n /= pow10_19;
|
||||
if ((n >> 64) == 0) return count_digits(static_cast<uint64_t>(n)) + 19;
|
||||
n /= pow10_19;
|
||||
return count_digits(static_cast<uint64_t>(n)) + 38;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Counts the number of digits in n. BITS = log2(radix).
|
||||
template <int BITS, typename UInt>
|
||||
FMT_CONSTEXPR auto count_digits(UInt n) -> int {
|
||||
@ -1231,6 +1239,26 @@ template <typename Char, typename UInt>
|
||||
FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
|
||||
-> Char* {
|
||||
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
|
||||
#if FMT_USE_INT128
|
||||
// Avoid slow 128-bit division by formatting 64-bit chunks of 19 digits each.
|
||||
if (!is_constant_evaluated() && num_bits<UInt>() == 128) {
|
||||
const uint64_t pow10_19 = 10000000000000000000ULL;
|
||||
auto v = static_cast<native_uint128>(value);
|
||||
unsigned pos = to_unsigned(size);
|
||||
while ((v >> 64) != 0) {
|
||||
auto chunk = static_cast<uint64_t>(v % pow10_19);
|
||||
v /= pow10_19;
|
||||
pos -= 19;
|
||||
fill_n(out + pos, 19, Char('0'));
|
||||
do_format_decimal(out + pos, chunk, 19);
|
||||
}
|
||||
auto top = static_cast<uint64_t>(v);
|
||||
int top_digits = count_digits(top);
|
||||
pos -= to_unsigned(top_digits);
|
||||
do_format_decimal(out + pos, top, top_digits);
|
||||
return out + pos;
|
||||
}
|
||||
#endif // FMT_USE_INT128
|
||||
unsigned n = to_unsigned(size);
|
||||
while (value >= 100) {
|
||||
n -= 2;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user