/* * 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 #include #include #include #include #include #include #include #include #include "stdstream_line_reader.h" using CLOCK = std::chrono::steady_clock; using TIME_PT = std::chrono::time_point; 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; 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 wxFileName& aFile, int aReps, BENCH_REPORT& report ) { std::string line; for( int i = 0; i < aReps; ++i) { std::ifstream fstr( aFile.GetFullPath().ToUTF8() ); 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 wxFileName& aFile, int aReps, BENCH_REPORT& report ) { std::string line; std::ifstream fstr( aFile.GetFullPath().ToUTF8() ); 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 static void bench_line_reader( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { for( int i = 0; i < aReps; ++i) { LR fstr( aFile.GetFullName() ); 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 static void bench_line_reader_reuse( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { LR fstr( aFile.GetFullName() ); 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 wxInputStream is recreated for each cycle. */ template static void bench_wxis( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { S fileStream( aFile.GetFullName() ); for( int i = 0; i < aReps; ++i) { INPUTSTREAM_LINE_READER istr( &fileStream, aFile.GetFullName() ); 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 wxInputStream is reset for each cycle. */ template static void bench_wxis_reuse( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { S fileStream( aFile.GetFullName() ); INPUTSTREAM_LINE_READER istr( &fileStream, aFile.GetFullName() ); for( int i = 0; i < aReps; ++i) { while( istr.ReadLine() ) { report.linesRead++; report.charAcc += (unsigned char) istr.Line()[0]; } fileStream.SeekI( 0 ); } } /** * Benchmark using a INPUTSTREAM_LINE_READER with a given * wxInputStream implementation, buffered with wxBufferedInputStream. * The wxInputStream is recreated for each cycle. */ template static void bench_wxbis( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { WXIS fileStream( aFile.GetFullName() ); wxBufferedInputStream bufferedStream( fileStream ); for( int i = 0; i < aReps; ++i) { INPUTSTREAM_LINE_READER istr( &bufferedStream, aFile.GetFullName() ); while( istr.ReadLine() ) { report.linesRead++; report.charAcc += (unsigned char) istr.Line()[0]; } fileStream.SeekI( 0 ); } } /** * Benchmark using a INPUTSTREAM_LINE_READER with a given * wxInputStream implementation, buffered with wxBufferedInputStream. * The wxInputStream is reset for each cycle. */ template static void bench_wxbis_reuse( const wxFileName& aFile, int aReps, BENCH_REPORT& report ) { WXIS fileStream( aFile.GetFullName() ); wxBufferedInputStream bufferedStream( fileStream ); INPUTSTREAM_LINE_READER istr( &bufferedStream, aFile.GetFullName() ); 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 benchmarkList = { { 'f', bench_fstream, "std::fstream" }, { 'F', bench_fstream_reuse, "std::fstream, reused" }, { 'r', bench_line_reader, "RICHIO" }, { 'R', bench_line_reader_reuse, "RICHIO, reused" }, { 'n', bench_line_reader, "std::ifstream L_R" }, { 'N', bench_line_reader_reuse, "std::ifstream L_R, reused" }, { 'w', bench_wxis, "wxFileIStream" }, { 'W', bench_wxis, "wxFileIStream, reused" }, { 'g', bench_wxis, "wxFFileIStream" }, { 'G', bench_wxis_reuse, "wxFFileIStream, reused" }, { 'b', bench_wxbis, "wxFileIStream. buf'd" }, { 'B', bench_wxbis_reuse, "wxFileIStream, buf'd, reused" }, { 'c', bench_wxbis, "wxFFileIStream. buf'd" }, { 'C', bench_wxbis_reuse, "wxFFileIStream, buf'd, 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 wxFileName& 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( 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] << " [" << getBenchFlags() << "]\n\n"; os << "Benchmarks:\n"; os << getBenchDescriptions(); return BAD_ARGS; } wxFileName 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.GetFullName() << 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( "%-30s %u lines, acc: %u in %u ms", bmark.name, report.linesRead, report.charAcc, (int) report.benchDurMs.count() ) << std::endl;; } return 0; }