Skip to content

Commit 9e60122

Browse files
committed
[libc++] Optimize std::getline
``` ----------------------------------------------- Benchmark old new ----------------------------------------------- BM_getline_string 318 ns 32.4 ns ```
1 parent 91c5de7 commit 9e60122

File tree

3 files changed

+76
-26
lines changed

3 files changed

+76
-26
lines changed

libcxx/include/istream

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,41 +1265,49 @@ _LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
12651265
getline(basic_istream<_CharT, _Traits>& __is, basic_string<_CharT, _Traits, _Allocator>& __str, _CharT __dlm) {
12661266
ios_base::iostate __state = ios_base::goodbit;
12671267
typename basic_istream<_CharT, _Traits>::sentry __sen(__is, true);
1268-
if (__sen) {
1268+
if (!__sen)
1269+
return __is;
12691270
# if _LIBCPP_HAS_EXCEPTIONS
1270-
try {
1271+
try {
12711272
# endif
1272-
__str.clear();
1273-
streamsize __extr = 0;
1274-
while (true) {
1275-
typename _Traits::int_type __i = __is.rdbuf()->sbumpc();
1276-
if (_Traits::eq_int_type(__i, _Traits::eof())) {
1277-
__state |= ios_base::eofbit;
1278-
break;
1279-
}
1280-
++__extr;
1281-
_CharT __ch = _Traits::to_char_type(__i);
1282-
if (_Traits::eq(__ch, __dlm))
1283-
break;
1284-
__str.push_back(__ch);
1285-
if (__str.size() == __str.max_size()) {
1273+
__str.clear();
1274+
1275+
auto& __buffer = *__is.rdbuf();
1276+
1277+
auto __next = __buffer.sgetc();
1278+
for (; !_Traits::eq_int_type(__next, _Traits::eof()); __next = __buffer.sgetc()) {
1279+
const auto* __first = __buffer.gptr();
1280+
const auto* __last = __buffer.egptr();
1281+
const auto* __match = _Traits::find(__first, __last - __first, __dlm);
1282+
if (__match) {
1283+
if (auto __cap = __str.max_size() - __str.size(); __cap <= static_cast<size_t>(__match - __first)) {
1284+
__str.append(__first, __cap);
1285+
__buffer.__gbump_ptrdiff(__cap);
12861286
__state |= ios_base::failbit;
12871287
break;
12881288
}
1289+
__str.append(__first, __match);
1290+
__buffer.__gbump_ptrdiff(__match - __first + 1);
1291+
break;
12891292
}
1290-
if (__extr == 0)
1291-
__state |= ios_base::failbit;
1293+
1294+
__str.append(__first, __last);
1295+
__buffer.__gbump_ptrdiff(__last - __first);
1296+
}
1297+
1298+
if (_Traits::eq_int_type(__next, _Traits::eof()))
1299+
__state |= ios_base::eofbit | (__str.empty() ? ios_base::failbit : ios_base::goodbit);
1300+
12921301
# if _LIBCPP_HAS_EXCEPTIONS
1293-
} catch (...) {
1294-
__state |= ios_base::badbit;
1295-
__is.__setstate_nothrow(__state);
1296-
if (__is.exceptions() & ios_base::badbit) {
1297-
throw;
1298-
}
1302+
} catch (...) {
1303+
__state |= ios_base::badbit;
1304+
__is.__setstate_nothrow(__state);
1305+
if (__is.exceptions() & ios_base::badbit) {
1306+
throw;
12991307
}
1300-
# endif
1301-
__is.setstate(__state);
13021308
}
1309+
# endif
1310+
__is.setstate(__state);
13031311
return __is;
13041312
}
13051313

libcxx/include/streambuf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ protected:
241241

242242
inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 void gbump(int __n) { __ninp_ += __n; }
243243

244+
// gbump takes an int, so it might not be able to represent the offset we want to add.
245+
_LIBCPP_HIDE_FROM_ABI void __gbump_ptrdiff(ptrdiff_t __n) { __ninp_ += __n; }
246+
244247
inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 void setg(char_type* __gbeg, char_type* __gnext, char_type* __gend) {
245248
_LIBCPP_ASSERT_VALID_INPUT_RANGE(std::__is_valid_range(__gbeg, __gnext), "[gbeg, gnext) must be a valid range");
246249
_LIBCPP_ASSERT_VALID_INPUT_RANGE(std::__is_valid_range(__gbeg, __gend), "[gbeg, gend) must be a valid range");
@@ -297,6 +300,10 @@ private:
297300
char_type* __bout_;
298301
char_type* __nout_;
299302
char_type* __eout_;
303+
304+
template <class _CharT2, class _Traits2, class _Allocator>
305+
_LIBCPP_HIDE_FROM_ABI friend basic_istream<_CharT2, _Traits2>&
306+
getline(basic_istream<_CharT2, _Traits2>&, basic_string<_CharT2, _Traits2, _Allocator>&, _CharT2);
300307
};
301308

302309
template <class _CharT, class _Traits>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
// UNSUPPORTED: c++03
11+
12+
#include <istream>
13+
#include <sstream>
14+
15+
#include <benchmark/benchmark.h>
16+
17+
void BM_getline_string(benchmark::State& state) {
18+
std::istringstream iss;
19+
20+
std::string str;
21+
str.reserve(128);
22+
iss.str("A long string to let getline do some more work, making sure that longer strings are parsed fast enough");
23+
24+
for (auto _ : state) {
25+
benchmark::DoNotOptimize(iss);
26+
27+
std::getline(iss, str);
28+
benchmark::DoNotOptimize(str);
29+
iss.seekg(0);
30+
}
31+
}
32+
33+
BENCHMARK(BM_getline_string);
34+
35+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)