[libcxx-commits] [libcxx] [libc++] Fix performance regression in std::num_put (PR #223334)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Sep 14 02:08:20 PDT 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/223334

None

>From 0486d19fe25a31a1dfc84ecdb94b3b63e8705d5b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 14 Sep 2026 11:08:00 +0200
Subject: [PATCH] [libc++] Fix performance regression in std::num_put

---
 libcxx/include/__locale_dir/pad_and_output.h  |  9 +-
 libcxx/include/locale                         |  1 +
 .../text/localization/num_put.bench.cpp       | 48 +++++++---
 .../facet.num.put.members/put_long.pass.cpp   | 89 +++++++++++++++++++
 4 files changed, 134 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__locale_dir/pad_and_output.h b/libcxx/include/__locale_dir/pad_and_output.h
index 4b845792a3087f..481961be6ed6e7 100644
--- a/libcxx/include/__locale_dir/pad_and_output.h
+++ b/libcxx/include/__locale_dir/pad_and_output.h
@@ -32,9 +32,12 @@ _LIBCPP_HIDE_FROM_ABI _OutputIterator __pad_and_output(
     __ns -= __sz;
   else
     __ns = 0;
-  __s = std::copy(__ob, __op, __s);
-  __s = std::fill_n(__s, __ns, __fl);
-  __s = std::copy(__op, __oe, __s);
+  if (__ob != __op)
+    __s = std::copy(__ob, __op, __s);
+  if (__ns != 0)
+    __s = std::fill_n(__s, __ns, __fl);
+  if (__op != __oe)
+    __s = std::copy(__op, __oe, __s);
   __iob.width(0);
   return __s;
 }
diff --git a/libcxx/include/locale b/libcxx/include/locale
index f0d6756338c41a..c3295ef92677bd 100644
--- a/libcxx/include/locale
+++ b/libcxx/include/locale
@@ -200,6 +200,7 @@ template <class charT> class messages_byname;
 #    include <__locale_dir/wbuffer_convert.h>
 #    include <__locale_dir/wstring_convert.h>
 #    include <ios>
+#    include <streambuf>
 #    include <version>
 
 #    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/test/benchmarks/text/localization/num_put.bench.cpp b/libcxx/test/benchmarks/text/localization/num_put.bench.cpp
index 329b139d74df61..9b56ae41597bec 100644
--- a/libcxx/test/benchmarks/text/localization/num_put.bench.cpp
+++ b/libcxx/test/benchmarks/text/localization/num_put.bench.cpp
@@ -16,25 +16,53 @@
 
 struct num_put : std::num_put<char, std::string::iterator> {};
 
-template <class T>
+class grouping_numpunct : public std::numpunct<char> {
+public:
+  grouping_numpunct() : std::numpunct<char>() {}
+
+protected:
+  virtual char_type do_thousands_sep() const { return '_'; }
+  virtual std::string do_grouping() const { return std::string("\1\2\3"); }
+};
+
+template <class NumPunct, class T>
 TEST_ALIGN_BENCHMARK void BM_num_put(benchmark::State& state) {
   auto val = T(123);
   std::ios ios(nullptr);
+  ios.imbue(std::locale(std::locale::classic(), new NumPunct));
+  ios.setf(static_cast<std::ios::fmtflags>(state.range()));
   num_put np;
 
+  std::string str('x', 10);
   for (auto _ : state) {
     benchmark::DoNotOptimize(val);
-    std::string str;
+    benchmark::DoNotOptimize(str);
+    if (state.range() != 0)
+      ios.width(10);
     benchmark::DoNotOptimize(np.put(str.begin(), ios, ' ', val));
   }
 }
-BENCHMARK(BM_num_put<bool>);
-BENCHMARK(BM_num_put<long>);
-BENCHMARK(BM_num_put<long long>);
-BENCHMARK(BM_num_put<unsigned long>);
-BENCHMARK(BM_num_put<unsigned long long>);
-BENCHMARK(BM_num_put<double>);
-BENCHMARK(BM_num_put<long double>);
-BENCHMARK(BM_num_put<const void*>);
+
+void bench_padding(benchmark::Benchmark* bm) {
+  bm->Arg(0)->Arg(std::ios::internal)->Arg(std::ios::left)->Arg(std::ios::right);
+}
+
+BENCHMARK(BM_num_put<std::numpunct<char>, bool>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, long long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, unsigned long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, unsigned long long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, double>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, long double>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<std::numpunct<char>, const void*>)->Apply(bench_padding);
+
+BENCHMARK(BM_num_put<grouping_numpunct, bool>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, long long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, unsigned long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, unsigned long long>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, double>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, long double>)->Apply(bench_padding);
+BENCHMARK(BM_num_put<grouping_numpunct, const void*>)->Apply(bench_padding);
 
 BENCHMARK_MAIN();
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long.pass.cpp
index 7f034d487e57ee..1042a1656cb43e 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long.pass.cpp
@@ -96,6 +96,47 @@ int main(int, char**) {
       std::string ex(str, base(iter));
       assert(ex == "+0");
     }
+    { // Ensure that padding is done properly without any grouping
+      {
+        std::ios ios(0);
+        ios.width(10);
+        long v = 1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "*********1");
+      }
+      {
+        std::ios ios(0);
+        ios.width(10);
+        ios.setf(std::ios::internal);
+        long v = -1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "-********1");
+      }
+      {
+        std::ios ios(0);
+        ios.width(10);
+        ios.setf(std::ios::left);
+        long v = -1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "-1********");
+      }
+      {
+        std::ios ios(0);
+        ios.width(10);
+        ios.setf(std::ios::right);
+        long v = -1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "********-1");
+      }
+    }
   }
   { // test octal
     {
@@ -137,6 +178,54 @@ int main(int, char**) {
       std::string ex(str, base(iter));
       assert(ex == "0");
     }
+    { // Ensure that padding is done properly without any grouping
+      {
+        std::ios ios(0);
+        std::oct(ios);
+        ios.width(10);
+        long v = 1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "*********1");
+      }
+      {
+        std::ios ios(0);
+        std::oct(ios);
+        std::showbase(ios);
+        ios.width(10);
+        ios.setf(std::ios::internal);
+        long v = 1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "********01");
+      }
+      {
+        std::ios ios(0);
+        std::oct(ios);
+        std::showbase(ios);
+        ios.width(10);
+        ios.setf(std::ios::left);
+        long v = 1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "01********");
+      }
+      {
+        std::ios ios(0);
+        std::oct(ios);
+        std::showbase(ios);
+        ios.width(10);
+        ios.setf(std::ios::right);
+        long v = 1;
+        char str[50];
+        cpp17_output_iterator<char*> iter = f.put(cpp17_output_iterator<char*>(str), ios, '*', v);
+        std::string ex(str, base(iter));
+        assert(ex == "********01");
+      }
+    }
   }
   { // test hexadecimal
     {



More information about the libcxx-commits mailing list