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

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 15 02:07:10 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

<details>
<summary>Changes</summary>

`__pad_and_output` calls `std::copy` and `std::fill_n` unconditionally, even though the calls are quite likely to do nothing. Adding conditions also allows the compiler to do jump-threading.

Fixes #<!-- -->178685

---
Full diff: https://github.com/llvm/llvm-project/pull/223334.diff


4 Files Affected:

- (modified) libcxx/include/__locale_dir/pad_and_output.h (+6-3) 
- (modified) libcxx/include/locale (+1) 
- (modified) libcxx/test/benchmarks/text/localization/num_put.bench.cpp (+38-10) 
- (modified) libcxx/test/std/localization/locale.categories/category.numeric/locale.nm.put/facet.num.put.members/put_long.pass.cpp (+89) 


``````````diff
diff --git a/libcxx/include/__locale_dir/pad_and_output.h b/libcxx/include/__locale_dir/pad_and_output.h
index 4b845792a3087..481961be6ed6e 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 f0d6756338c41..c3295ef92677b 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 329b139d74df6..9b56ae41597be 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 7f034d487e57e..1042a1656cb43 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
     {

``````````

</details>


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


More information about the libcxx-commits mailing list