[libc++] Optimize using std::copy with an ostreambuf_iterator (#181815)
``` Benchmark old new Difference % Difference ---------------------------------------------- -------------- -------------- ------------ -------------- std::copy(CharT*,_CharT*,_ostreambuf_iterator) 8115.45 329.54 -7785.91 -95.94% ```
This commit is contained in:
parent
23dffff410
commit
b101f01382
@ -46,6 +46,8 @@ Improvements and New Features
|
||||
- The ``std::ranges::fold_left_with_iter`` algorithm has been optimized for
|
||||
segmented iterators, resulting in a performance improvement of up to 1.38x
|
||||
for ``std::deque<int>`` iterators.
|
||||
- ``std::copy(CharT*, CharT*, ostreambuf_iterator<CharT>)`` has been optimized, resulting in performance improvements
|
||||
of up to 25x.
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#ifndef _LIBCPP___ITERATOR_OSTREAMBUF_ITERATOR_H
|
||||
#define _LIBCPP___ITERATOR_OSTREAMBUF_ITERATOR_H
|
||||
|
||||
#include <__algorithm/specialized_algorithms.h>
|
||||
#include <__config>
|
||||
#include <__cstddef/ptrdiff_t.h>
|
||||
#include <__fwd/ios.h>
|
||||
@ -17,6 +18,8 @@
|
||||
#include <__fwd/streambuf.h>
|
||||
#include <__iterator/iterator.h>
|
||||
#include <__iterator/iterator_traits.h>
|
||||
#include <__type_traits/is_same.h>
|
||||
#include <__utility/pair.h>
|
||||
#include <iosfwd> // for forward declaration of ostreambuf_iterator
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
@ -64,6 +67,26 @@ public:
|
||||
friend _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_Ch, _Tr> __pad_and_output(
|
||||
ostreambuf_iterator<_Ch, _Tr> __s, const _Ch* __ob, const _Ch* __op, const _Ch* __oe, ios_base& __iob, _Ch __fl);
|
||||
#endif // _LIBCPP_HAS_LOCALIZATION
|
||||
|
||||
template <class, class...>
|
||||
friend struct __specialized_algorithm;
|
||||
};
|
||||
|
||||
template <class _InCharT, class _CharT, class _Traits>
|
||||
struct __specialized_algorithm<_Algorithm::__copy,
|
||||
__iterator_pair<_InCharT*, _InCharT*>,
|
||||
__single_iterator<ostreambuf_iterator<_CharT, _Traits> > > {
|
||||
static const bool __has_algorithm = is_same<const _CharT, const _InCharT>::value;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI static pair<_InCharT*, ostreambuf_iterator<_CharT, _Traits> >
|
||||
operator()(_InCharT* __first, _InCharT* __last, ostreambuf_iterator<_CharT, _Traits> __result) {
|
||||
auto __size = __last - __first;
|
||||
if (__result.__sbuf_ && __size > 0) {
|
||||
if (__result.__sbuf_->sputn(__first, __last - __first) != __size)
|
||||
__result.__sbuf_ = nullptr;
|
||||
}
|
||||
return pair<_InCharT*, ostreambuf_iterator<_CharT, _Traits> >(__last, __result);
|
||||
}
|
||||
};
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
@ -55,13 +55,7 @@ _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_CharT, _Traits> __pad_and_output(
|
||||
__ns -= __sz;
|
||||
else
|
||||
__ns = 0;
|
||||
streamsize __np = __op - __ob;
|
||||
if (__np > 0) {
|
||||
if (__s.__sbuf_->sputn(__ob, __np) != __np) {
|
||||
__s.__sbuf_ = nullptr;
|
||||
return __s;
|
||||
}
|
||||
}
|
||||
__s = std::copy(__ob, __op, __s);
|
||||
if (__ns > 0) {
|
||||
basic_string<_CharT, _Traits> __sp(__ns, __fl);
|
||||
if (__s.__sbuf_->sputn(__sp.data(), __ns) != __ns) {
|
||||
@ -69,13 +63,7 @@ _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_CharT, _Traits> __pad_and_output(
|
||||
return __s;
|
||||
}
|
||||
}
|
||||
__np = __oe - __op;
|
||||
if (__np > 0) {
|
||||
if (__s.__sbuf_->sputn(__op, __np) != __np) {
|
||||
__s.__sbuf_ = nullptr;
|
||||
return __s;
|
||||
}
|
||||
}
|
||||
__s = std::copy(__op, __oe, __s);
|
||||
__iob.width(0);
|
||||
return __s;
|
||||
}
|
||||
|
||||
26
libcxx/test/benchmarks/streams/copy.bench.cpp
Normal file
26
libcxx/test/benchmarks/streams/copy.bench.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 <iterator>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
static void bm_copy(benchmark::State& state) {
|
||||
std::vector<char> buffer;
|
||||
buffer.resize(16384);
|
||||
|
||||
std::ofstream stream("/dev/null");
|
||||
|
||||
for (auto _ : state)
|
||||
std::copy(buffer.begin(), buffer.end(), std::ostreambuf_iterator<char>(stream.rdbuf()));
|
||||
}
|
||||
BENCHMARK(bm_copy)->Name("std::copy(CharT*, CharT*, ostreambuf_iterator)");
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
@ -13,6 +13,7 @@
|
||||
// ADDITIONAL_COMPILE_FLAGS: -Wno-c++14-extensions -Wno-c++17-extensions
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@ -81,3 +82,14 @@ static_assert(has_alg<Alg::__for_each, iter_pair<const_iter, const_iter>>);
|
||||
static_assert(has_alg<Alg::__for_each, single_range<std::multimap<int, int>>>);
|
||||
#endif
|
||||
} // namespace multimap
|
||||
|
||||
namespace ostreambuf_iterator {
|
||||
template <class CharT>
|
||||
using obi = std::ostreambuf_iterator<CharT>;
|
||||
static_assert(has_alg<Alg::__copy, iter_pair<char*, char*>, single_iter<obi<char>>>);
|
||||
static_assert(has_alg<Alg::__copy, iter_pair<const char*, const char*>, single_iter<obi<char>>>);
|
||||
static_assert(has_alg<Alg::__copy, iter_pair<wchar_t*, wchar_t*>, single_iter<obi<wchar_t>>>);
|
||||
static_assert(has_alg<Alg::__copy, iter_pair<const wchar_t*, const wchar_t*>, single_iter<obi<wchar_t>>>);
|
||||
static_assert(!has_alg<Alg::__copy, iter_pair<char*, char*>, single_iter<obi<wchar_t>>>);
|
||||
|
||||
} // namespace ostreambuf_iterator
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <algorithm>
|
||||
|
||||
// template<class CharT, class Traits>
|
||||
// constexpr OutIter
|
||||
// copy(CharT* first, CharT* last, ostreambuf_iterator<CharT, Traits> result);
|
||||
|
||||
// UNSUPPORTED: no-localization
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "stream_types.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
template <class CCharT>
|
||||
void test() {
|
||||
using CharT = typename std::remove_cv<CCharT>::type;
|
||||
{
|
||||
std::basic_ostringstream<CharT> oss;
|
||||
CCharT buff[] = {'B', 'a', 'n', 'a', 'n', 'e'};
|
||||
std::copy(std::begin(buff), std::end(buff), std::ostreambuf_iterator<CharT>(oss));
|
||||
assert(oss.str() == std::basic_string_view<CharT>(buff, 6));
|
||||
}
|
||||
{
|
||||
failing_streambuf<CharT> fsb(4);
|
||||
std::basic_ostream<CharT> oss(&fsb);
|
||||
CCharT buff[] = {'B', 'a', 'n', 'a', 'n', 'e'};
|
||||
auto res = std::copy(std::begin(buff), std::end(buff), std::ostreambuf_iterator<CharT>(oss));
|
||||
assert(res.failed());
|
||||
assert(fsb.str() == std::basic_string_view<CharT>(buff, 4));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
test<const char>();
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
test<const wchar_t>();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -41,4 +41,30 @@ private:
|
||||
size_t index_;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
class failing_streambuf : public std::basic_streambuf<CharT> {
|
||||
using char_type = CharT;
|
||||
using traits_type = std::char_traits<CharT>;
|
||||
using int_type = typename traits_type::int_type;
|
||||
|
||||
public:
|
||||
failing_streambuf(size_t fail_at) : fail_at_(fail_at) {}
|
||||
|
||||
std::basic_string<char_type> str() const { return underlying_data_; }
|
||||
|
||||
protected:
|
||||
int_type overflow(int_type c) override {
|
||||
if (underlying_data_.size() == fail_at_)
|
||||
return traits_type::eof();
|
||||
if (traits_type::eq_int_type(c, traits_type::eof()))
|
||||
return traits_type::not_eof(c);
|
||||
underlying_data_.push_back(traits_type::to_char_type(c));
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_string<char_type> underlying_data_;
|
||||
size_t fail_at_;
|
||||
};
|
||||
|
||||
#endif // TEST_SUPPORT_STREAM_TYPES_H
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user