IO benchmark program: RICHIO vs std::fstream and wxStream
This compares the performance of RICHIO line readers against other implementations, and can be used for profiling and optimisation. Current benchmarks provided: * richio FILE_LINE_READER * Raw std::ifstream (no LINE_READER wrapper), using getline (so no line length limiting) * LINE_READER wrapper around std::istream, with a std::ifstream implementation * Existing richio wxInputStream wrappers (with File and FFile implemntations)
This commit is contained in:
parent
35c7974a96
commit
bea0a9ab6e
|
@ -35,3 +35,5 @@ add_executable( property_tree
|
|||
target_link_libraries( property_tree
|
||||
${wxWidgets_LIBRARIES}
|
||||
)
|
||||
|
||||
add_subdirectory( io_benchmark )
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
include_directories( BEFORE ${INC_BEFORE} )
|
||||
|
||||
set( IOBENCHMARK_SRCS
|
||||
io_benchmark.cpp
|
||||
stdstream_line_reader.cpp
|
||||
)
|
||||
|
||||
add_executable( io_benchmark
|
||||
${IOBENCHMARK_SRCS}
|
||||
)
|
||||
|
||||
target_link_libraries( io_benchmark
|
||||
common
|
||||
${wxWidgets_LIBRARIES}
|
||||
)
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program 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
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <richio.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <ios>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#include "stdstream_line_reader.h"
|
||||
|
||||
|
||||
using CLOCK = std::chrono::steady_clock;
|
||||
using TIME_PT = std::chrono::time_point<CLOCK>;
|
||||
|
||||
|
||||
struct BENCH_REPORT
|
||||
{
|
||||
unsigned linesRead;
|
||||
|
||||
/**
|
||||
* Char accumulator, used to prevent compilers optimising away
|
||||
* otherwise unused line buffers, and also as a primitive sanity
|
||||
* check that the same lines were read
|
||||
*/
|
||||
unsigned charAcc;
|
||||
|
||||
std::chrono::milliseconds benchDurMs;
|
||||
};
|
||||
|
||||
|
||||
using BENCH_FUNC = std::function<void(const wxString&, int, BENCH_REPORT&)>;
|
||||
|
||||
|
||||
struct BENCHMARK
|
||||
{
|
||||
char triggerChar;
|
||||
BENCH_FUNC func;
|
||||
wxString name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using a raw std::ifstream, with no LINE_READER
|
||||
* wrapper. The stream is recreated for each cycle.
|
||||
*/
|
||||
static void bench_fstream( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
std::string line;
|
||||
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
std::ifstream fstr( aFile );
|
||||
|
||||
while( getline( fstr, line ) )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) line[0];
|
||||
}
|
||||
|
||||
fstr.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using a raw std::ifstream, with no LINE_READER
|
||||
* wrapper. The stream is not recreated for each cycle, just reset.
|
||||
*/
|
||||
static void bench_fstream_reuse( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
std::string line;
|
||||
std::ifstream fstr( aFile );
|
||||
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
while( getline( fstr, line ) )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) line[0];
|
||||
}
|
||||
fstr.clear() ;
|
||||
fstr.seekg(0, std::ios::beg) ;
|
||||
}
|
||||
|
||||
fstr.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using a given LINE_READER implementation.
|
||||
* The LINE_READER is recreated for each cycle.
|
||||
*/
|
||||
template<typename LR>
|
||||
static void bench_line_reader( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
LR fstr( aFile );
|
||||
while( fstr.ReadLine() )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) fstr.Line()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using a given LINE_READER implementation.
|
||||
* The LINE_READER is rewound for each cycle, not recreated.
|
||||
*/
|
||||
template<typename LR>
|
||||
static void bench_line_reader_reuse( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
LR fstr( aFile );
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
|
||||
while( fstr.ReadLine() )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) fstr.Line()[0];
|
||||
}
|
||||
|
||||
fstr.Rewind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using an INPUTSTREAM_LINE_READER with a given
|
||||
* wxInputStream implementation.
|
||||
* The INPUTSTREAM_LINE_READER is reset for each cycle.
|
||||
*/
|
||||
template<typename S>
|
||||
static void bench_wxis( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
S fileStream( aFile );
|
||||
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
INPUTSTREAM_LINE_READER istr( &fileStream, aFile );
|
||||
|
||||
while( istr.ReadLine() )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) istr.Line()[0];
|
||||
}
|
||||
|
||||
fileStream.SeekI( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Benchmark using an INPUTSTREAM_LINE_READER with a given
|
||||
* wxInputStream implementation.
|
||||
* The INPUTSTREAM_LINE_READER is recreated for each cycle.
|
||||
*/
|
||||
template<typename S>
|
||||
static void bench_wxis_reuse( const wxString& aFile, int aReps, BENCH_REPORT& report )
|
||||
{
|
||||
S fileStream( aFile );
|
||||
INPUTSTREAM_LINE_READER istr( &fileStream, aFile );
|
||||
|
||||
for( int i = 0; i < aReps; ++i)
|
||||
{
|
||||
while( istr.ReadLine() )
|
||||
{
|
||||
report.linesRead++;
|
||||
report.charAcc += (unsigned char) istr.Line()[0];
|
||||
}
|
||||
|
||||
fileStream.SeekI( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of available benchmarks
|
||||
*/
|
||||
static std::vector<BENCHMARK> benchmarkList =
|
||||
{
|
||||
{ 'f', bench_fstream, "std::fstream" },
|
||||
{ 'F', bench_fstream_reuse, "std::fstream, reused" },
|
||||
{ 'r', bench_line_reader<FILE_LINE_READER>, "RICHIO" },
|
||||
{ 'R', bench_line_reader_reuse<FILE_LINE_READER>, "RICHIO, reused" },
|
||||
{ 'n', bench_line_reader<IFSTREAM_LINE_READER>, "std::ifstream L_R" },
|
||||
{ 'N', bench_line_reader_reuse<IFSTREAM_LINE_READER>, "std::ifstream L_R, reused" },
|
||||
{ 'w', bench_wxis<wxFileInputStream>, "wxFileIStream" },
|
||||
{ 'W', bench_wxis<wxFileInputStream>, "wxFileIStream, reused" },
|
||||
{ 'g', bench_wxis<wxFFileInputStream>, "wxFFileIStream" },
|
||||
{ 'G', bench_wxis_reuse<wxFFileInputStream>, "wxFFileIStream, reused" },
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Construct string of all flags used for specifying benchmarks
|
||||
* on the command line
|
||||
*/
|
||||
static wxString getBenchFlags()
|
||||
{
|
||||
wxString flags;
|
||||
|
||||
for( auto& bmark : benchmarkList )
|
||||
{
|
||||
flags << bmark.triggerChar;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Usage description of a benchmakr spec
|
||||
*/
|
||||
static wxString getBenchDescriptions()
|
||||
{
|
||||
wxString desc;
|
||||
|
||||
for( auto& bmark : benchmarkList )
|
||||
{
|
||||
desc << " " << bmark.triggerChar << ": " << bmark.name << "\n";
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
||||
BENCH_REPORT executeBenchMark( const BENCHMARK& aBenchmark, int aReps,
|
||||
const wxString& aFilename )
|
||||
{
|
||||
BENCH_REPORT report = {};
|
||||
|
||||
TIME_PT start = CLOCK::now();
|
||||
aBenchmark.func( aFilename, aReps, report );
|
||||
TIME_PT end = CLOCK::now();
|
||||
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::duration_cast;
|
||||
|
||||
report.benchDurMs = duration_cast<milliseconds>( end - start );
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
enum RET_CODES
|
||||
{
|
||||
BAD_ARGS = 1,
|
||||
};
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
auto& os = std::cout;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
os << "Usage: " << argv[0] << " <FILE> <REPS> [" << getBenchFlags() << "]\n\n";
|
||||
os << "Benchmarks:\n";
|
||||
os << getBenchDescriptions();
|
||||
return BAD_ARGS;
|
||||
}
|
||||
|
||||
wxString inFile( argv[1] );
|
||||
|
||||
long reps = 0;
|
||||
wxString( argv[2] ).ToLong( &reps );
|
||||
|
||||
// get the benchmark to do, or all of them if nothing given
|
||||
wxString bench;
|
||||
if ( argc == 4 )
|
||||
bench = argv[3];
|
||||
|
||||
os << "IO Bench Mark Util" << std::endl;
|
||||
|
||||
os << " Benchmark file: " << inFile << std::endl;
|
||||
os << " Repetitions: " << (int) reps << std::endl;
|
||||
os << std::endl;
|
||||
|
||||
for( auto& bmark : benchmarkList )
|
||||
{
|
||||
if( bench.size() && !bench.Contains( bmark.triggerChar ) )
|
||||
continue;
|
||||
|
||||
BENCH_REPORT report = executeBenchMark( bmark, reps, inFile );
|
||||
|
||||
os << wxString::Format( "%-25s %u lines, acc: %u in %u ms",
|
||||
bmark.name, report.linesRead, report.charAcc, (int) report.benchDurMs.count() )
|
||||
<< std::endl;;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program 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
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "stdstream_line_reader.h"
|
||||
|
||||
#include <ios>
|
||||
|
||||
STDISTREAM_LINE_READER::STDISTREAM_LINE_READER() :
|
||||
LINE_READER( 0 ),
|
||||
m_stream( nullptr )
|
||||
{
|
||||
line = nullptr;
|
||||
lineNum = 0;
|
||||
}
|
||||
|
||||
|
||||
STDISTREAM_LINE_READER::~STDISTREAM_LINE_READER()
|
||||
{
|
||||
// this is only a view into a string, it cant be deleted by the base
|
||||
line = nullptr;
|
||||
}
|
||||
|
||||
|
||||
char* STDISTREAM_LINE_READER::ReadLine() throw( IO_ERROR )
|
||||
{
|
||||
getline( *m_stream, m_buffer );
|
||||
|
||||
m_buffer.append( 1, '\n' );
|
||||
|
||||
length = m_buffer.size();
|
||||
line = (char*) m_buffer.data(); //ew why no const??
|
||||
|
||||
// lineNum is incremented even if there was no line read, because this
|
||||
// leads to better error reporting when we hit an end of file.
|
||||
++lineNum;
|
||||
|
||||
return m_stream->eof() ? nullptr : line;
|
||||
}
|
||||
|
||||
|
||||
void STDISTREAM_LINE_READER::setStream( std::istream& aStream )
|
||||
{
|
||||
// Could be done with a virtual getStream function, but the
|
||||
// virtual function call is a noticable (but minor) penalty within
|
||||
// ReadLine() in tight loops
|
||||
m_stream = &aStream;
|
||||
}
|
||||
|
||||
|
||||
IFSTREAM_LINE_READER::IFSTREAM_LINE_READER( const wxString& aFileName ) throw( IO_ERROR ) :
|
||||
m_fStream( std::ifstream( aFileName ) )
|
||||
{
|
||||
if( !m_fStream.is_open() )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "Unable to open filename '%s' for reading" ), aFileName.GetData() );
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
setStream( m_fStream );
|
||||
|
||||
source = aFileName;
|
||||
}
|
||||
|
||||
|
||||
void IFSTREAM_LINE_READER::Rewind()
|
||||
{
|
||||
m_fStream.clear() ;
|
||||
m_fStream.seekg(0, std::ios::beg );
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program 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
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef FSTREAM_LINE_READER_H
|
||||
#define FSTREAM_LINE_READER_H
|
||||
|
||||
#include <richio.h>
|
||||
|
||||
#include <istream>
|
||||
#include <fstream>
|
||||
|
||||
/**
|
||||
* LINE_READER that wraps a given std::istream instance.
|
||||
*/
|
||||
class STDISTREAM_LINE_READER : public LINE_READER
|
||||
{
|
||||
public:
|
||||
|
||||
STDISTREAM_LINE_READER();
|
||||
|
||||
~STDISTREAM_LINE_READER();
|
||||
|
||||
char* ReadLine() throw( IO_ERROR ) override;
|
||||
|
||||
protected:
|
||||
|
||||
void setStream( std::istream& aStream );
|
||||
|
||||
private:
|
||||
std::string m_buffer;
|
||||
std::istream* m_stream;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* LINE_READER interface backed by std::ifstream
|
||||
*/
|
||||
class IFSTREAM_LINE_READER : public STDISTREAM_LINE_READER
|
||||
{
|
||||
public:
|
||||
|
||||
IFSTREAM_LINE_READER( const wxString& aFileName ) throw( IO_ERROR );
|
||||
|
||||
void Rewind();
|
||||
|
||||
private:
|
||||
std::ifstream m_fStream;
|
||||
};
|
||||
|
||||
#endif // FSTREAM_LINE_READER_H
|
Loading…
Reference in New Issue