[libcxx-commits] [libcxx] [libc++] Optimize string to integer functions (PR #166076)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Nov 2 08:30:09 PST 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/166076
None
>From 022468b8583d2001f50a4228b1ac181fc466e138 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 2 Nov 2025 17:29:46 +0100
Subject: [PATCH] [libc++] Optimize string to integer functions
---
libcxx/src/string.cpp | 89 ++++++++----------
.../containers/string_conversion.bench.cpp | 94 +++++++++++++++++++
2 files changed, 133 insertions(+), 50 deletions(-)
create mode 100644 libcxx/test/benchmarks/containers/string_conversion.bench.cpp
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index 5028fc88fe46d..287a99db39be6 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -81,8 +81,8 @@ template string operator+ <char, char_traits<char>, allocator<char>>(char const*
namespace {
-inline void throw_from_string_out_of_range(const string& func) {
- std::__throw_out_of_range((func + ": out of range").c_str());
+inline void throw_from_string_out_of_range(const char* func) {
+ std::__throw_out_of_range((std::string(func) + ": out of range").c_str());
}
inline void throw_from_string_invalid_arg(const string& func) {
@@ -92,7 +92,7 @@ inline void throw_from_string_invalid_arg(const string& func) {
// as_integer
template <typename V, typename S, typename F>
-inline V as_integer_helper(const string& func, const S& str, size_t* idx, int base, F f) {
+inline V as_integer_helper(const char* func, const S& str, size_t* idx, int base, F f) {
typename S::value_type* ptr = nullptr;
const typename S::value_type* const p = str.c_str();
__libcpp_remove_reference_t<decltype(errno)> errno_save = errno;
@@ -109,42 +109,14 @@ inline V as_integer_helper(const string& func, const S& str, size_t* idx, int ba
}
template <typename V, typename S>
-inline V as_integer(const string& func, const S& s, size_t* idx, int base);
+inline V as_integer(const char* func, const S& s, size_t* idx, int base);
// string
-template <>
-inline int as_integer(const string& func, const string& s, size_t* idx, int base) {
- // Use long as no Standard string to integer exists.
- long r = as_integer_helper<long>(func, s, idx, base, strtol);
- if (r < numeric_limits<int>::min() || numeric_limits<int>::max() < r)
- throw_from_string_out_of_range(func);
- return static_cast<int>(r);
-}
-
-template <>
-inline long as_integer(const string& func, const string& s, size_t* idx, int base) {
- return as_integer_helper<long>(func, s, idx, base, strtol);
-}
-
-template <>
-inline unsigned long as_integer(const string& func, const string& s, size_t* idx, int base) {
- return as_integer_helper<unsigned long>(func, s, idx, base, strtoul);
-}
-
-template <>
-inline long long as_integer(const string& func, const string& s, size_t* idx, int base) {
- return as_integer_helper<long long>(func, s, idx, base, strtoll);
-}
-
-template <>
-inline unsigned long long as_integer(const string& func, const string& s, size_t* idx, int base) {
- return as_integer_helper<unsigned long long>(func, s, idx, base, strtoull);
-}
#if _LIBCPP_HAS_WIDE_CHARACTERS
// wstring
template <>
-inline int as_integer(const string& func, const wstring& s, size_t* idx, int base) {
+inline int as_integer(const char* func, const wstring& s, size_t* idx, int base) {
// Use long as no Stantard string to integer exists.
long r = as_integer_helper<long>(func, s, idx, base, wcstol);
if (r < numeric_limits<int>::min() || numeric_limits<int>::max() < r)
@@ -153,22 +125,22 @@ inline int as_integer(const string& func, const wstring& s, size_t* idx, int bas
}
template <>
-inline long as_integer(const string& func, const wstring& s, size_t* idx, int base) {
+inline long as_integer(const char* func, const wstring& s, size_t* idx, int base) {
return as_integer_helper<long>(func, s, idx, base, wcstol);
}
template <>
-inline unsigned long as_integer(const string& func, const wstring& s, size_t* idx, int base) {
+inline unsigned long as_integer(const char* func, const wstring& s, size_t* idx, int base) {
return as_integer_helper<unsigned long>(func, s, idx, base, wcstoul);
}
template <>
-inline long long as_integer(const string& func, const wstring& s, size_t* idx, int base) {
+inline long long as_integer(const char* func, const wstring& s, size_t* idx, int base) {
return as_integer_helper<long long>(func, s, idx, base, wcstoll);
}
template <>
-inline unsigned long long as_integer(const string& func, const wstring& s, size_t* idx, int base) {
+inline unsigned long long as_integer(const char* func, const wstring& s, size_t* idx, int base) {
return as_integer_helper<unsigned long long>(func, s, idx, base, wcstoull);
}
#endif // _LIBCPP_HAS_WIDE_CHARACTERS
@@ -176,7 +148,7 @@ inline unsigned long long as_integer(const string& func, const wstring& s, size_
// as_float
template <typename V, typename S, typename F>
-inline V as_float_helper(const string& func, const S& str, size_t* idx, F f) {
+inline V as_float_helper(const char* func, const S& str, size_t* idx, F f) {
typename S::value_type* ptr = nullptr;
const typename S::value_type* const p = str.c_str();
__libcpp_remove_reference_t<decltype(errno)> errno_save = errno;
@@ -193,54 +165,71 @@ inline V as_float_helper(const string& func, const S& str, size_t* idx, F f) {
}
template <typename V, typename S>
-inline V as_float(const string& func, const S& s, size_t* idx = nullptr);
+inline V as_float(const char* func, const S& s, size_t* idx = nullptr);
template <>
-inline float as_float(const string& func, const string& s, size_t* idx) {
+inline float as_float(const char* func, const string& s, size_t* idx) {
return as_float_helper<float>(func, s, idx, strtof);
}
template <>
-inline double as_float(const string& func, const string& s, size_t* idx) {
+inline double as_float(const char* func, const string& s, size_t* idx) {
return as_float_helper<double>(func, s, idx, strtod);
}
template <>
-inline long double as_float(const string& func, const string& s, size_t* idx) {
+inline long double as_float(const char* func, const string& s, size_t* idx) {
return as_float_helper<long double>(func, s, idx, strtold);
}
#if _LIBCPP_HAS_WIDE_CHARACTERS
template <>
-inline float as_float(const string& func, const wstring& s, size_t* idx) {
+inline float as_float(const char* func, const wstring& s, size_t* idx) {
return as_float_helper<float>(func, s, idx, wcstof);
}
template <>
-inline double as_float(const string& func, const wstring& s, size_t* idx) {
+inline double as_float(const char* func, const wstring& s, size_t* idx) {
return as_float_helper<double>(func, s, idx, wcstod);
}
template <>
-inline long double as_float(const string& func, const wstring& s, size_t* idx) {
+inline long double as_float(const char* func, const wstring& s, size_t* idx) {
return as_float_helper<long double>(func, s, idx, wcstold);
}
#endif // _LIBCPP_HAS_WIDE_CHARACTERS
+template <class Integer>
+Integer string_to_integer_impl(const char* func_name, const string& str, size_t* index, int base) {
+ Integer result;
+ from_chars_result status = std::from_chars(str.data(), str.data() + str.size(), result, base);
+ if (status.ec != std::errc()) {
+ if (status.ec == std::errc::result_out_of_range)
+ throw_from_string_out_of_range(func_name);
+ throw_from_string_invalid_arg(func_name);
+ }
+ if (index)
+ *index = status.ptr - str.data();
+
+ return result;
+}
+
} // unnamed namespace
-int stoi(const string& str, size_t* idx, int base) { return as_integer<int>("stoi", str, idx, base); }
+int stoi(const string& str, size_t* idx, int base) { return string_to_integer_impl<int>("stoi", str, idx, base); }
-long stol(const string& str, size_t* idx, int base) { return as_integer<long>("stol", str, idx, base); }
+long stol(const string& str, size_t* idx, int base) { return string_to_integer_impl<long>("stol", str, idx, base); }
unsigned long stoul(const string& str, size_t* idx, int base) {
- return as_integer<unsigned long>("stoul", str, idx, base);
+ return string_to_integer_impl<unsigned long>("stoul", str, idx, base);
}
-long long stoll(const string& str, size_t* idx, int base) { return as_integer<long long>("stoll", str, idx, base); }
+long long stoll(const string& str, size_t* idx, int base) {
+ return string_to_integer_impl<long long>("stoll", str, idx, base);
+}
unsigned long long stoull(const string& str, size_t* idx, int base) {
- return as_integer<unsigned long long>("stoull", str, idx, base);
+ return string_to_integer_impl<unsigned long long>("stoull", str, idx, base);
}
float stof(const string& str, size_t* idx) { return as_float<float>("stof", str, idx); }
diff --git a/libcxx/test/benchmarks/containers/string_conversion.bench.cpp b/libcxx/test/benchmarks/containers/string_conversion.bench.cpp
new file mode 100644
index 0000000000000..ee3a38ecdef40
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/string_conversion.bench.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+#include <limits>
+#include <string>
+
+#include <benchmark/benchmark.h>
+
+template <class CharT>
+void string_to_arithmetic_impl(int& out, const std::basic_string<CharT>& str) {
+ out = std::stoi(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(long& out, const std::basic_string<CharT>& str) {
+ out = std::stol(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(long long& out, const std::basic_string<CharT>& str) {
+ out = std::stoll(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(unsigned long& out, const std::basic_string<CharT>& str) {
+ out = std::stoul(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(unsigned long long& out, const std::basic_string<CharT>& str) {
+ out = std::stoull(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(float& out, const std::basic_string<CharT>& str) {
+ out = std::stof(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(double& out, const std::basic_string<CharT>& str) {
+ out = std::stod(str);
+}
+
+template <class CharT>
+void string_to_arithmetic_impl(long double& out, const std::basic_string<CharT>& str) {
+ out = std::stold(str);
+}
+
+template <class Integer>
+std::string to_string_dispatch(char, Integer i) {
+ return std::to_string(i);
+}
+
+template <class Integer>
+std::wstring to_string_dispatch(wchar_t, Integer i) {
+ return std::to_wstring(i);
+}
+
+template <class CharT, class Integer>
+void BM_string_to_arithmetic(benchmark::State& state) {
+ std::basic_string<CharT> num = to_string_dispatch(CharT(), std::numeric_limits<Integer>::max());
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(num);
+ Integer val;
+ string_to_arithmetic_impl(val, num);
+ }
+}
+
+BENCHMARK(BM_string_to_arithmetic<char, int>);
+BENCHMARK(BM_string_to_arithmetic<char, long>);
+BENCHMARK(BM_string_to_arithmetic<char, long long>);
+BENCHMARK(BM_string_to_arithmetic<char, unsigned long>);
+BENCHMARK(BM_string_to_arithmetic<char, unsigned long long>);
+BENCHMARK(BM_string_to_arithmetic<char, float>);
+BENCHMARK(BM_string_to_arithmetic<char, double>);
+BENCHMARK(BM_string_to_arithmetic<char, long double>);
+BENCHMARK(BM_string_to_arithmetic<wchar_t, int>);
+BENCHMARK(BM_string_to_arithmetic<wchar_t, long>);
+BENCHMARK(BM_string_to_arithmetic<wchar_t, long long>);
+BENCHMARK(BM_string_to_arithmetic<wchar_t, unsigned long>);
+BENCHMARK(BM_string_to_arithmetic<wchar_t, unsigned long long>);
+BENCHMARK(BM_string_to_arithmetic<char, float>);
+BENCHMARK(BM_string_to_arithmetic<char, double>);
+BENCHMARK(BM_string_to_arithmetic<char, long double>);
+
+BENCHMARK_MAIN();
More information about the libcxx-commits
mailing list