From c56e3d82fc9090d1580ca8dc02a6add848629d0c Mon Sep 17 00:00:00 2001 From: Paolo Carlini Date: Mon, 13 Sep 2004 22:21:34 +0000 Subject: [PATCH] re PR libstdc++/11722 ([3.4 only] Unbuffered filebuf::sgetn is slow) 2004-09-13 Paolo Carlini PR libstdc++/11722 * include/std/std_fstream.h (xsgetn): Declare only. * include/bits/fstream.tcc (xsgetn): Define, optimize for the always_noconv() case: when __n > __buflen, copy the available buffer and issue a direct read. * testsuite/performance/27_io/filebuf_sgetn_unbuf.cc: New. * include/bits/fstream.tcc (xsputn): Minor tweak, reorder a conditional. From-SVN: r87453 --- libstdc++-v3/ChangeLog | 14 ++- libstdc++-v3/include/bits/fstream.tcc | 69 ++++++++++++++- libstdc++-v3/include/std/std_fstream.h | 19 +---- .../performance/27_io/filebuf_sgetn_unbuf.cc | 85 +++++++++++++++++++ 4 files changed, 166 insertions(+), 21 deletions(-) create mode 100644 libstdc++-v3/testsuite/performance/27_io/filebuf_sgetn_unbuf.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index e75bb689491..16faad2e92c 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,4 +1,16 @@ -2004-09-14 Hans-Peter Nilsson +2004-09-13 Paolo Carlini + + PR libstdc++/11722 + * include/std/std_fstream.h (xsgetn): Declare only. + * include/bits/fstream.tcc (xsgetn): Define, optimize for the + always_noconv() case: when __n > __buflen, copy the available + buffer and issue a direct read. + * testsuite/performance/27_io/filebuf_sgetn_unbuf.cc: New. + + * include/bits/fstream.tcc (xsputn): Minor tweak, reorder a + conditional. + +2004-09-13 Hans-Peter Nilsson * testsuite/lib/libstdc++.exp: Use gcc wrapper.exp and call libstdc++_maybe_build_wrapper instead of using local code. diff --git a/libstdc++-v3/include/bits/fstream.tcc b/libstdc++-v3/include/bits/fstream.tcc index 6c2e1822adb..542dc6e8bc4 100644 --- a/libstdc++-v3/include/bits/fstream.tcc +++ b/libstdc++-v3/include/bits/fstream.tcc @@ -494,6 +494,71 @@ namespace std return __elen == __plen; } + template + streamsize + basic_filebuf<_CharT, _Traits>:: + xsgetn(_CharT* __s, streamsize __n) + { + // Clear out pback buffer before going on to the real deal... + streamsize __ret = 0; + if (this->_M_pback_init) + { + if (__n > 0 && this->gptr() == this->eback()) + { + *__s++ = *this->gptr(); + this->gbump(1); + __ret = 1; + --__n; + } + _M_destroy_pback(); + } + + // Optimization in the always_noconv() case, to be generalized in the + // future: when __n > __buflen we read directly instead of using the + // buffer repeatedly. + const bool __testin = this->_M_mode & ios_base::in; + const streamsize __buflen = this->_M_buf_size > 1 ? this->_M_buf_size - 1 + : 1; + if (__n > __buflen && __check_facet(_M_codecvt).always_noconv() + && __testin && !_M_writing) + { + // First, copy the chars already present in the buffer. + const streamsize __avail = this->egptr() - this->gptr(); + if (__avail == 1) + *__s = *this->gptr(); + else if (__avail > 1) + traits_type::move(__s, this->gptr(), __avail); + __s += __avail; + this->gbump(__avail); + __ret += __avail; + __n -= __avail; + + const streamsize __len = _M_file.xsgetn(reinterpret_cast(__s), + __n); + if (__len == -1) + __throw_ios_failure(__N("basic_filebuf::xsgetn " + "error reading the file")); + __ret += __len; + if (__len == __n) + { + _M_set_buffer(0); + _M_reading = true; + } + else if (__len == 0) + { + // If end of file is reached, set 'uncommitted' + // mode, thus allowing an immediate write without + // an intervening seek. + _M_set_buffer(-1); + _M_reading = false; + } + } + else + __ret += __streambuf_type::xsgetn(__s, __n); + + return __ret; + } + template streamsize basic_filebuf<_CharT, _Traits>:: @@ -504,8 +569,8 @@ namespace std // using the buffer. streamsize __ret = 0; const bool __testout = this->_M_mode & ios_base::out; - if (__testout && !_M_reading - && __check_facet(_M_codecvt).always_noconv()) + if (__check_facet(_M_codecvt).always_noconv() + && __testout && !_M_reading) { // Measurement would reveal the best choice. const streamsize __chunk = 1ul << 10; diff --git a/libstdc++-v3/include/std/std_fstream.h b/libstdc++-v3/include/std/std_fstream.h index 3bb26382cbe..f962a2b202a 100644 --- a/libstdc++-v3/include/std/std_fstream.h +++ b/libstdc++-v3/include/std/std_fstream.h @@ -419,24 +419,7 @@ namespace std // [documentation is inherited] virtual streamsize - xsgetn(char_type* __s, streamsize __n) - { - // Clear out pback buffer before going on to the real deal... - streamsize __ret = 0; - if (this->_M_pback_init) - { - if (__n && this->gptr() == this->eback()) - { - *__s++ = *this->gptr(); - this->gbump(1); - __ret = 1; - } - _M_destroy_pback(); - } - if (__ret < __n) - __ret += __streambuf_type::xsgetn(__s, __n - __ret); - return __ret; - } + xsgetn(char_type* __s, streamsize __n); // [documentation is inherited] virtual streamsize diff --git a/libstdc++-v3/testsuite/performance/27_io/filebuf_sgetn_unbuf.cc b/libstdc++-v3/testsuite/performance/27_io/filebuf_sgetn_unbuf.cc new file mode 100644 index 00000000000..5741ab308bb --- /dev/null +++ b/libstdc++-v3/testsuite/performance/27_io/filebuf_sgetn_unbuf.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2004 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING. If not, write to the Free +// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. + +// As a special exception, you may use this file as part of a free software +// library without restriction. Specifically, if other files instantiate +// templates or use macros or inline functions from this file, or you compile +// this file and link it with other files to produce an executable, this +// file does not by itself cause the resulting executable to be covered by +// the GNU General Public License. This exception does not however +// invalidate any other reasons why the executable file might be covered by +// the GNU General Public License. + +#include +#include +#include + +// libstdc++/11722 +int main() +{ + using namespace std; + using namespace __gnu_test; + + time_counter time; + resource_counter resource; + + const int iterations = 500000; + const int chunksize = 100; + + char* chunk = new char[chunksize]; + const char* name = "/usr/share/dict/linux.words"; + + // C + FILE* file = fopen(name, "r"); + setvbuf(file, 0, _IONBF, 0); + start_counters(time, resource); + for (int i = 0; i < iterations; ++i) + if (fread(chunk, 1, chunksize, file) < chunksize) + fseek(file, 0, SEEK_SET); + stop_counters(time, resource); + fclose(file); + report_performance(__FILE__, "C", time, resource); + clear_counters(time, resource); + + // C unlocked + file = fopen(name, "r"); + setvbuf(file, 0, _IONBF, 0); + start_counters(time, resource); + for (int i = 0; i < iterations; ++i) + if (fread_unlocked(chunk, 1, chunksize, file) < chunksize) + fseek(file, 0, SEEK_SET); + stop_counters(time, resource); + fclose(file); + report_performance(__FILE__, "C unlocked", time, resource); + clear_counters(time, resource); + + // C++ + filebuf buf; + buf.pubsetbuf(0, 0); + buf.open(name, ios_base::in); + start_counters(time, resource); + for (int i = 0; i < iterations; ++i) + if (buf.sgetn(chunk, chunksize) < chunksize) + buf.pubseekoff(0, ios::beg); + stop_counters(time, resource); + report_performance(__FILE__, "C++", time, resource); + + delete [] chunk; + + return 0; +} -- 2.30.2