[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