Skip to content

Commit 5db43c7

Browse files
committed
[libc++] Optimize ofstream::write
1 parent 9315d70 commit 5db43c7

File tree

7 files changed

+193
-3
lines changed

7 files changed

+193
-3
lines changed

libcxx/docs/ReleaseNotes/22.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Improvements and New Features
4646
- The performance of ``map::map(const map&)`` has been improved up to 2.3x
4747
- The performance of ``map::operator=(const map&)`` has been improved by up to 11x
4848

49+
- ``ofstream::write`` has been optimized to pass through large strings to system calls directly instead of copying them
50+
in chunks into a buffer.
51+
4952
Deprecations and Removals
5053
-------------------------
5154

libcxx/include/fstream

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ protected:
299299
int sync() override;
300300
void imbue(const locale& __loc) override;
301301

302+
_LIBCPP_HIDE_FROM_ABI_VIRTUAL streamsize xsputn(const char_type* __str, streamsize __len) override {
303+
if (__always_noconv_ && __len >= (this->epptr() - this->pbase())) {
304+
if (traits_type::eq_int_type(overflow(), traits_type::eof()))
305+
return 0;
306+
307+
return std::fwrite(__str, sizeof(char_type), __len, __file_);
308+
}
309+
return basic_streambuf<_CharT, _Traits>::xsputn(__str, __len);
310+
}
311+
302312
private:
303313
char* __extbuf_;
304314
const char* __extbufnext_;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <fstream>
10+
#include <vector>
11+
12+
#include <benchmark/benchmark.h>
13+
14+
static void bm_write(benchmark::State& state) {
15+
std::vector<char> buffer;
16+
buffer.resize(16384);
17+
18+
std::ofstream stream("/dev/null");
19+
20+
for (auto _ : state)
21+
stream.write(buffer.data(), buffer.size());
22+
}
23+
BENCHMARK(bm_write);
24+
25+
BENCHMARK_MAIN();

libcxx/test/libcxx/input.output/file.streams/fstreams/filebuf/traits_mismatch.verify.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
// UNSUPPORTED: no-wide-characters
1717

18+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
19+
1820
#include <fstream>
1921

2022
std::basic_filebuf<char, std::char_traits<wchar_t> > f;
2123
// expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
22-
// expected-error@*:* 9 {{only virtual member functions can be marked 'override'}}
24+
// expected-error@*:* 10 {{only virtual member functions can be marked 'override'}}

libcxx/test/libcxx/input.output/file.streams/fstreams/traits_mismatch.verify.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515

1616
// UNSUPPORTED: no-wide-characters
1717

18+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
19+
1820
#include <fstream>
1921

2022
std::basic_fstream<char, std::char_traits<wchar_t> > f;
2123
// expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
2224
// expected-error-re@*:* {{static assertion failed{{.*}}traits_type::char_type must be the same type as CharT}}
2325

24-
// expected-error@*:* 11 {{only virtual member functions can be marked 'override'}}
26+
// expected-error@*:* 12 {{only virtual member functions can be marked 'override'}}
2527

2628
// FIXME: As of commit r324062 Clang incorrectly generates a diagnostic about mismatching
2729
// exception specifications for types which are already invalid for one reason or another.

libcxx/test/std/input.output/file.streams/fstreams/filebuf.virtuals/seekoff.pass.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ int main(int, char**)
2929
| std::ios_base::trunc) != 0);
3030
assert(f.is_open());
3131
f.sputn("abcdefghijklmnopqrstuvwxyz", 26);
32-
LIBCPP_ASSERT(buf[0] == 'v');
3332
pos_type p = f.pubseekoff(-15, std::ios_base::cur);
3433
assert(p == 11);
3534
assert(f.sgetc() == 'l');
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03
10+
11+
// <fstream>
12+
13+
// streamsize xsputn(const char_type*, streamsize) override;
14+
15+
// This isn't a required override by the standard, but most implementations override it, since it allows for
16+
// significantly improved performance in some cases. All of this code is required to work, so this isn't a libc++
17+
// extension
18+
19+
#include <algorithm>
20+
#include <cassert>
21+
#include <codecvt>
22+
#include <cstring>
23+
#include <fstream>
24+
#include <locale>
25+
#include <vector>
26+
27+
#include "test_macros.h"
28+
29+
typedef std::filebuf::pos_type pos_type;
30+
typedef std::filebuf::off_type off_type;
31+
32+
void sputn_seekoff(char* buf,
33+
const size_t buf_size,
34+
const std::streamsize chunk_size1,
35+
const off_type offset1,
36+
const std::streamsize chunk_size2) {
37+
std::string data{"abcdefghijklmnopqrstuvwxyz"};
38+
const std::streamsize data_size = static_cast<std::streamsize>(data.size());
39+
assert(chunk_size1 <= data_size);
40+
assert(chunk_size2 <= data_size);
41+
// vector with expected data in the file to be written
42+
std::size_t result_size = 5 + chunk_size1 + chunk_size2 + 1;
43+
if (offset1 > 0) {
44+
result_size += offset1;
45+
}
46+
std::vector<char> result(result_size, 0);
47+
{
48+
std::filebuf f;
49+
f.pubsetbuf(buf, buf_size);
50+
assert(f.open("sputn_seekoff.dat", std::ios_base::out) != 0);
51+
assert(f.is_open());
52+
53+
assert(f.pubseekoff(off_type(5), std::ios_base::beg) = off_type(5));
54+
55+
std::vector<char> chunk(data.begin() + 5, data.begin() + 5 + chunk_size1);
56+
std::copy(chunk.begin(), chunk.end(), result.begin() + 5);
57+
const std::streamsize len1 = f.sputn(chunk.data(), chunk_size1);
58+
assert(len1 == chunk_size1);
59+
// check that nothing in the original chunk was modified by sputn()
60+
assert(std::strncmp(chunk.data(), data.substr(5, len1).c_str(), len1) == 0);
61+
62+
pos_type p1 = f.pubseekoff(offset1, std::ios_base::cur);
63+
char c;
64+
if (p1 < 0) {
65+
p1 = f.pubseekoff(0, std::ios_base::beg);
66+
assert(p1 == 0);
67+
c = '^';
68+
} else {
69+
assert(p1 == 5 + len1 + offset1);
70+
if (p1 > data_size) {
71+
c = '_';
72+
} else {
73+
c = data[p1];
74+
}
75+
}
76+
77+
result[p1] = c;
78+
assert(f.sputc(c) == c);
79+
80+
f.pubseekpos(std::ios_base::beg);
81+
result[0] = 'A';
82+
assert(f.sputc(toupper(data[0])) == 'A');
83+
84+
pos_type end_pos = f.pubseekoff(off_type(0), std::ios_base::end);
85+
assert(f.sputc(toupper(data[data_size - 1])) == 'Z');
86+
result[end_pos] = 'Z';
87+
88+
assert(f.pubseekpos(p1) == p1);
89+
result[p1] = toupper(c);
90+
assert(f.sputc(toupper(c)) == toupper(c));
91+
92+
pos_type new_pos = result_size - chunk_size2;
93+
pos_type p2 = f.pubseekoff(new_pos, std::ios_base::beg);
94+
assert(p2 == new_pos);
95+
chunk = std::vector<char>(data.end() - chunk_size2, data.end());
96+
std::copy(chunk.begin(), chunk.end(), result.begin() + p2);
97+
const std::streamsize len2 = f.sputn(chunk.data(), chunk_size2);
98+
assert(len2 == chunk_size2);
99+
assert(std::strncmp(chunk.data(), data.substr(data_size - chunk_size2, chunk_size2).c_str(), len2) == 0);
100+
f.close();
101+
}
102+
std::filebuf f;
103+
assert(f.open("sputn_seekoff.dat", std::ios_base::in) != 0);
104+
assert(f.is_open());
105+
std::vector<char> check(result.size(), -1);
106+
const std::size_t len = f.sgetn(check.data(), check.size());
107+
assert(len == result.size());
108+
for (size_t i = 0; i < len; ++i) {
109+
assert(check[i] == result[i]);
110+
}
111+
}
112+
113+
void sputn_not_open() {
114+
std::vector<char> data(10, 'a');
115+
std::filebuf f;
116+
std::streamsize len = f.sputn(data.data(), data.size());
117+
assert(len == 0);
118+
assert(std::strncmp(data.data(), "aaaaaaaaaa", 10) == 0);
119+
}
120+
121+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
122+
void sputn_not_open_wchar() {
123+
std::vector<wchar_t> data(10, L'a');
124+
std::wfilebuf f;
125+
std::streamsize len = f.sputn(data.data(), data.size());
126+
assert(len == 0);
127+
assert(std::wcsncmp(data.data(), L"aaaaaaaaaa", 10) == 0);
128+
}
129+
#endif
130+
131+
int main(int, char**) {
132+
sputn_not_open();
133+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
134+
sputn_not_open_wchar();
135+
#endif
136+
137+
sputn_seekoff(nullptr, 10, 22, -27, 1);
138+
sputn_seekoff(nullptr, 10, 1, -27, 1);
139+
sputn_seekoff(nullptr, 10, 10, 14, 12);
140+
sputn_seekoff(nullptr, 10, 1, -2, 1);
141+
sputn_seekoff(nullptr, 10, 10, -4, 12);
142+
sputn_seekoff(nullptr, 10, 11, -12, 3);
143+
sputn_seekoff(nullptr, 10, 7, 3, 8);
144+
sputn_seekoff(nullptr, 10, 5, -5, 12);
145+
sputn_seekoff(nullptr, 10, 1, 1, 1);
146+
sputn_seekoff(nullptr, 10, 9, 0, 1);
147+
148+
return 0;
149+
}

0 commit comments

Comments
 (0)