[libcxx-commits] [libcxx] c5949f7 - [libc++] Optimize ofstream::write (#123337)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 27 05:50:40 PDT 2025


Author: Nikolas Klauser
Date: 2025-08-27T14:50:37+02:00
New Revision: c5949f74bf8cd6f7b5e0a94c49b80c78ca9d78cc

URL: https://github.com/llvm/llvm-project/commit/c5949f74bf8cd6f7b5e0a94c49b80c78ca9d78cc
DIFF: https://github.com/llvm/llvm-project/commit/c5949f74bf8cd6f7b5e0a94c49b80c78ca9d78cc.diff

LOG: [libc++] Optimize ofstream::write (#123337)

```
----------------------------
Benchmark       old      new
----------------------------
bm_write    1382 ns   521 ns
```

Added: 
    libcxx/test/benchmarks/streams/ofstream.bench.cpp
    libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/xsputn.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes/22.rst
    libcxx/include/fstream
    libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
    libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
    libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 5378655c66606..4ad5452cc29cc 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -58,6 +58,9 @@ Improvements and New Features
 - The performance of ``insert(iterator, iterator)`` of ``multimap`` and ``multiset`` has been improved by up to 2.5x
 - The performance of ``erase(iterator, iterator)`` in the unordered containers has been improved by up to 1.9x
 
+- ``ofstream::write`` has been optimized to pass through large strings to system calls directly instead of copying them
+  in chunks into a buffer.
+
 Deprecations and Removals
 -------------------------
 

diff  --git a/libcxx/include/fstream b/libcxx/include/fstream
index 6d3f20fff688f..04cebdedc31d8 100644
--- a/libcxx/include/fstream
+++ b/libcxx/include/fstream
@@ -299,6 +299,16 @@ protected:
   int sync() override;
   void imbue(const locale& __loc) override;
 
+  _LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsputn(const char_type* __str, streamsize __len) override {
+    if (__always_noconv_ && __len >= (this->epptr() - this->pbase())) {
+      if (traits_type::eq_int_type(overflow(), traits_type::eof()))
+        return 0;
+
+      return std::fwrite(__str, sizeof(char_type), __len, __file_);
+    }
+    return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
+  }
+
 private:
   char* __extbuf_;
   const char* __extbufnext_;

diff  --git a/libcxx/test/benchmarks/streams/ofstream.bench.cpp b/libcxx/test/benchmarks/streams/ofstream.bench.cpp
new file mode 100644
index 0000000000000..60606a9d67e2f
--- /dev/null
+++ b/libcxx/test/benchmarks/streams/ofstream.bench.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <fstream>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+static void bm_write(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream stream("/dev/null");
+
+  for (auto _ : state)
+    stream.write(buffer.data(), buffer.size());
+}
+BENCHMARK(bm_write);
+
+BENCHMARK_MAIN();

diff  --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
index 455c9979ae57d..fcbf6497c8d77 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp
@@ -15,8 +15,10 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <fstream>
 
 std::basic_filebuf<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
-// expected-error@*:* 9 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}

diff  --git a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
index cc52cc119d50e..8eca76c037bfe 100644
--- a/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
+++ b/libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp
@@ -15,13 +15,15 @@
 
 // UNSUPPORTED: no-wide-characters
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 #include <fstream>
 
 std::basic_fstream<char, std::char_traits<wchar_t> > f;
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 // expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
 
-// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
+// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
 
 // FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
 // exception specifications for types which are already invalid for one reason or another.

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
index 8008901802e91..f6378e7998ee9 100644
--- a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp
@@ -29,7 +29,6 @@ int main(int, char**)
                                                        | std::ios_base::trunc) != 0);
         assert(f.is_open());
         f.sputn("abcdefghijklmnopqrstuvwxyz", 26);
-        LIBCPP_ASSERT(buf[0] == 'v');
         pos_type p = f.pubseekoff(-15, std::ios_base::cur);
         assert(p == 11);
         assert(f.sgetc() == 'l');

diff  --git a/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/xsputn.pass.cpp b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/xsputn.pass.cpp
new file mode 100644
index 0000000000000..2d6be5cf527a1
--- /dev/null
+++ b/libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/xsputn.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <fstream>
+
+// streamsize xsputn(const char_type*, streamsize) override;
+
+// This isn't a required override by the standard, but most implementations override it, since it allows for
+// significantly improved performance in some cases. All of this code is required to work, so this isn't a libc++
+// extension
+
+#include <algorithm>
+#include <cassert>
+#include <codecvt>
+#include <cstring>
+#include <fstream>
+#include <locale>
+#include <vector>
+
+#include "test_macros.h"
+
+typedef std::filebuf::pos_type pos_type;
+typedef std::filebuf::off_type off_type;
+
+void sputn_seekoff(char* buf,
+                   const size_t buf_size,
+                   const std::streamsize chunk_size1,
+                   const off_type offset1,
+                   const std::streamsize chunk_size2) {
+  std::string data{"abcdefghijklmnopqrstuvwxyz"};
+  const std::streamsize data_size = static_cast<std::streamsize>(data.size());
+  assert(chunk_size1 <= data_size);
+  assert(chunk_size2 <= data_size);
+  // vector with expected data in the file to be written
+  std::size_t result_size = 5 + chunk_size1 + chunk_size2 + 1;
+  if (offset1 > 0) {
+    result_size += offset1;
+  }
+  std::vector<char> result(result_size, 0);
+  {
+    std::filebuf f;
+    f.pubsetbuf(buf, buf_size);
+    assert(f.open("sputn_seekoff.dat", std::ios_base::out) != 0);
+    assert(f.is_open());
+
+    assert(f.pubseekoff(off_type(5), std::ios_base::beg) = off_type(5));
+
+    std::vector<char> chunk(data.begin() + 5, data.begin() + 5 + chunk_size1);
+    std::copy(chunk.begin(), chunk.end(), result.begin() + 5);
+    const std::streamsize len1 = f.sputn(chunk.data(), chunk_size1);
+    assert(len1 == chunk_size1);
+    // check that nothing in the original chunk was modified by sputn()
+    assert(std::strncmp(chunk.data(), data.substr(5, len1).c_str(), len1) == 0);
+
+    pos_type p1 = f.pubseekoff(offset1, std::ios_base::cur);
+    char c;
+    if (p1 < 0) {
+      p1 = f.pubseekoff(0, std::ios_base::beg);
+      assert(p1 == 0);
+      c = '^';
+    } else {
+      assert(p1 == 5 + len1 + offset1);
+      if (p1 > data_size) {
+        c = '_';
+      } else {
+        c = data[p1];
+      }
+    }
+
+    result[p1] = c;
+    assert(f.sputc(c) == c);
+
+    f.pubseekpos(std::ios_base::beg);
+    result[0] = 'A';
+    assert(f.sputc(toupper(data[0])) == 'A');
+
+    pos_type end_pos = f.pubseekoff(off_type(0), std::ios_base::end);
+    assert(f.sputc(toupper(data[data_size - 1])) == 'Z');
+    result[end_pos] = 'Z';
+
+    assert(f.pubseekpos(p1) == p1);
+    result[p1] = toupper(c);
+    assert(f.sputc(toupper(c)) == toupper(c));
+
+    pos_type new_pos = result_size - chunk_size2;
+    pos_type p2      = f.pubseekoff(new_pos, std::ios_base::beg);
+    assert(p2 == new_pos);
+    chunk = std::vector<char>(data.end() - chunk_size2, data.end());
+    std::copy(chunk.begin(), chunk.end(), result.begin() + p2);
+    const std::streamsize len2 = f.sputn(chunk.data(), chunk_size2);
+    assert(len2 == chunk_size2);
+    assert(std::strncmp(chunk.data(), data.substr(data_size - chunk_size2, chunk_size2).c_str(), len2) == 0);
+    f.close();
+  }
+  std::filebuf f;
+  assert(f.open("sputn_seekoff.dat", std::ios_base::in) != 0);
+  assert(f.is_open());
+  std::vector<char> check(result.size(), -1);
+  const std::size_t len = f.sgetn(check.data(), check.size());
+  assert(len == result.size());
+  for (size_t i = 0; i < len; ++i) {
+    assert(check[i] == result[i]);
+  }
+}
+
+void sputn_not_open() {
+  std::vector<char> data(10, 'a');
+  std::filebuf f;
+  std::streamsize len = f.sputn(data.data(), data.size());
+  assert(len == 0);
+  assert(std::strncmp(data.data(), "aaaaaaaaaa", 10) == 0);
+}
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+void sputn_not_open_wchar() {
+  std::vector<wchar_t> data(10, L'a');
+  std::wfilebuf f;
+  std::streamsize len = f.sputn(data.data(), data.size());
+  assert(len == 0);
+  assert(std::wcsncmp(data.data(), L"aaaaaaaaaa", 10) == 0);
+}
+#endif
+
+int main(int, char**) {
+  sputn_not_open();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  sputn_not_open_wchar();
+#endif
+
+  sputn_seekoff(nullptr, 10, 21, -27, 1);
+  sputn_seekoff(nullptr, 10, 1, -27, 1);
+  sputn_seekoff(nullptr, 10, 10, 14, 12);
+  sputn_seekoff(nullptr, 10, 1, -2, 1);
+  sputn_seekoff(nullptr, 10, 10, -4, 12);
+  sputn_seekoff(nullptr, 10, 11, -12, 3);
+  sputn_seekoff(nullptr, 10, 7, 3, 8);
+  sputn_seekoff(nullptr, 10, 5, -5, 12);
+  sputn_seekoff(nullptr, 10, 1, 1, 1);
+  sputn_seekoff(nullptr, 10, 9, 0, 1);
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list