Commit a6d9a9cd by Abseil Team Committed by Copybara-Service

Performance improvements for `absl::AsciiStrToLower` and `absl::AsciiStrToUpper`.

PiperOrigin-RevId: 575969640
Change-Id: If6ddc0a71debfe571c2739ec91fc99594bc36f88
parent a0b72adc
...@@ -159,6 +159,20 @@ ABSL_DLL const char kToUpper[256] = { ...@@ -159,6 +159,20 @@ ABSL_DLL const char kToUpper[256] = {
}; };
// clang-format on // clang-format on
// Returns whether `c` is in the a-z/A-Z range (w.r.t. `ToUpper`).
// Implemented by:
// 1. Pushing the a-z/A-Z range to [SCHAR_MIN, SCHAR_MIN + 26).
// 2. Comparing to SCHAR_MIN + 26.
template <bool ToUpper>
constexpr bool AsciiInAZRange(unsigned char c) {
constexpr unsigned char sub = (ToUpper ? 'a' : 'A') - SCHAR_MIN;
constexpr signed char threshold = SCHAR_MIN + 26; // 26 = alphabet size.
// Using unsigned arithmetic as overflows/underflows are well defined.
unsigned char u = c - sub;
// Using signed cmp, as SIMD unsigned cmp isn't available in many platforms.
return static_cast<signed char>(u) < threshold;
}
template <bool ToUpper> template <bool ToUpper>
constexpr void AsciiStrCaseFold(char* p, char* end) { constexpr void AsciiStrCaseFold(char* p, char* end) {
// The upper- and lowercase versions of ASCII characters differ by only 1 bit. // The upper- and lowercase versions of ASCII characters differ by only 1 bit.
...@@ -168,15 +182,9 @@ constexpr void AsciiStrCaseFold(char* p, char* end) { ...@@ -168,15 +182,9 @@ constexpr void AsciiStrCaseFold(char* p, char* end) {
// have the same single bit difference. // have the same single bit difference.
constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A'; constexpr unsigned char kAsciiCaseBitFlip = 'a' ^ 'A';
constexpr char ch_a = ToUpper ? 'a' : 'A';
constexpr char ch_z = ToUpper ? 'z' : 'Z';
for (; p < end; ++p) { for (; p < end; ++p) {
unsigned char v = static_cast<unsigned char>(*p); unsigned char v = static_cast<unsigned char>(*p);
// We use & instead of && to ensure this always stays branchless v ^= AsciiInAZRange<ToUpper>(v) ? kAsciiCaseBitFlip : 0;
// We use static_cast<int> to suppress -Wbitwise-instead-of-logical
bool is_in_range = static_cast<bool>(static_cast<int>(ch_a <= v) &
static_cast<int>(v <= ch_z));
v ^= is_in_range ? kAsciiCaseBitFlip : 0;
*p = static_cast<char>(v); *p = static_cast<char>(v);
} }
} }
......
...@@ -105,18 +105,28 @@ static void BM_StrToLower(benchmark::State& state) { ...@@ -105,18 +105,28 @@ static void BM_StrToLower(benchmark::State& state) {
const int size = state.range(0); const int size = state.range(0);
std::string s(size, 'X'); std::string s(size, 'X');
for (auto _ : state) { for (auto _ : state) {
benchmark::DoNotOptimize(absl::AsciiStrToLower(s)); benchmark::DoNotOptimize(s);
std::string res = absl::AsciiStrToLower(s);
benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK(BM_StrToLower)->Range(1, 1 << 20); BENCHMARK(BM_StrToLower)
->DenseRange(0, 32)
->RangeMultiplier(2)
->Range(64, 1 << 20);
static void BM_StrToUpper(benchmark::State& state) { static void BM_StrToUpper(benchmark::State& state) {
const int size = state.range(0); const int size = state.range(0);
std::string s(size, 'x'); std::string s(size, 'x');
for (auto _ : state) { for (auto _ : state) {
benchmark::DoNotOptimize(absl::AsciiStrToUpper(s)); benchmark::DoNotOptimize(s);
std::string res = absl::AsciiStrToUpper(s);
benchmark::DoNotOptimize(res);
} }
} }
BENCHMARK(BM_StrToUpper)->Range(1, 1 << 20); BENCHMARK(BM_StrToUpper)
->DenseRange(0, 32)
->RangeMultiplier(2)
->Range(64, 1 << 20);
} // namespace } // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment