//////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2005 by Andrei Alexandrescu // Permission to use, copy, modify, distribute, and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author makes no representations about the suitability of this software // for any purpose. It is provided "as is" without express or implied // warranty. //////////////////////////////////////////////////////////////////////////////// // $Id$ #include #include #include #include #include #include #include "../SmallObj/timer.h" #include "ThreadPool.hpp" #if defined(_MSC_VER) #if _MSC_VER >= 1400 #define sprintf sprintf_s #define _snprintf _snprintf_s #else #define sprintf sprintf #define _snprintf _snprintf #endif #endif using namespace std; using namespace Loki; template Integral2 RandomInt(Integral1 low, Integral2 up) { // From ``Accelerated C++'', page 135: // random integer in the range [0, n) // We adjust to generate in the range [0, n] const Integral2 low2 = low, n = up - low; assert(n > 0); const size_t bucket_size = RAND_MAX / n; assert(bucket_size > 0); Integral2 r; do r = Integral2(rand() / bucket_size); while (r > n); r = r + low2; assert(r >= low2 && r <= up); return r; } string RandomString(unsigned int maxSize) { string result(RandomInt(0, maxSize), '\0'); unsigned int i = 0; for (; i != result.size(); ++i) { result[i] = RandomInt('a', 'z'); } return result; } template void TestCase(const string& fmt, T value) { char buf[4096]; std::string s; const int i1 = SPrintf(s, fmt.c_str())(value); #ifdef _MSC_VER const int i2 = _snprintf(buf, sizeof(buf), fmt.c_str(), value); #else const int i2 = snprintf(buf, sizeof(buf), fmt.c_str(), value); #endif if (i1 != i2 || s != buf) { cout << endl << "Reference: " << i2 << "; Actual: " << i1 << ", Difference = " << i2-i1 << endl << "V: [" << value << "]" << endl << "F: [" << fmt << "]" << endl << "R: [" << buf << "]" << endl << "A: [" << s.c_str() << "]" << endl; assert(false); } } template void TestCase2(const string& fmt, T value, U value2) { char buf[4096]; std::string s; const int i1 = SPrintf(s, fmt.c_str())(value)(value2); const int i2 = snprintf(buf, sizeof(buf), fmt.c_str(), value, value2); assert(i1 == i2); assert(s == buf); } // ---------------------------------------------------------------------------- void test_dword() { typedef signed int Int; typedef unsigned int UInt; typedef signed long Long; typedef unsigned long ULong; Int i(0); UInt ui(0); Long l(0); ULong ul(0); Printf("%d")(i); Printf("%d")(ui); Printf("%d")(l); Printf("%d")(ul); } // ---------------------------------------------------------------------------- void SpeedTest( unsigned int loop ) { cout << "Starting SpeedTest" << endl; // test speed Timer t; if(loop < 100) loop = 100; t.start(); for (int i=loop; i > 0; --i) printf("Hey, %u frobnicators and %u twiddlicators\n",i, i); t.stop(); t.t100 = t.t(); int t_printf = t.t(); t.start(); for (int i=loop; i > 0; --i) ::Loki::Printf("Hey, %u frobnicators and %u twiddlicators\n")(i)(i); t.stop(); int t_Printf = t.t(); t.start(); for (int i=loop; i > 0; --i) cout << "Hey, " << i << " frobnicators and " << i <<" twiddlicators\n"; t.stop(); int t_cout = t.t(); Printf("\n\nElapsed time for %i outputs\n\n")(loop); t.print(t_printf,"printf : "); t.print(t_Printf,"Printf : "); t.print(t_cout, "std::cout: "); cout << "Finished SpeedTest" << endl; } // ---------------------------------------------------------------------------- void FormatTest( void ) { cout << "Starting FormatTest" << endl; string result; unsigned int i = 1; signed short ss = 2; float f = 3.4F; const char * message = " over here! "; const char * format = ""; ::Loki::SPrintf( result, format ); assert( result == format ); result.clear(); format = "abc"; ::Loki::SPrintf( result, format ); assert( result == format ); result.clear(); format = "%%"; ::Loki::SPrintf( result, format ); assert( result == "%" ); result.clear(); format = "%d"; ::Loki::SPrintf( result, format )( i ); assert( result == "1" ); result.clear(); format = "%s"; ::Loki::SPrintf( result, format )( message ); assert( result == message ); result.clear(); format = "%d"; try { ::Loki::SPrintf( result, format )( i )( message ); assert( false ); } catch ( const ::std::logic_error & ex ) { (void)ex; assert( true ); } result.clear(); format = "%d"; try { ::Loki::SPrintf( result, format )( i )( message )( ss ); assert( false ); } catch ( const ::std::logic_error & ex ) { (void)ex; assert( true ); } result.clear(); format = "%d"; try { ::Loki::SPrintf( result, format )( i )( ss ); assert( false ); } catch ( const ::std::logic_error & ex ) { (void)ex; assert( true ); } result.clear(); format = "%d"; try { ::Loki::SPrintf( result, format )( i )( f ); assert( false ); } catch ( const ::std::logic_error & ex ) { (void)ex; assert( true ); } result.clear(); cout << "Finished FormatTest" << endl; } // ---------------------------------------------------------------------------- void RandomTest( unsigned int loopCount ) { cout << "Starting RandomTest" << endl; if ( loopCount < 100 ) loopCount = 100; //srand(time(0)); srand(0); printf("\nNumber of tests:\n"); for ( unsigned int i = 0; i < loopCount; ++i ) { printf("%u\r", i); // Generate a random string for the head string lead = RandomString(100); // This string will hold a random format specification string formatSpec(lead + "|%"); // Generate a random set of flags static const string flags("-+0 #"); size_t maxFlags = RandomInt(0u, flags.length() - 1); for (size_t i = 0; i != maxFlags; ++i) { formatSpec += flags[RandomInt(0u, flags.length() - 1)]; } // Generate an optional random width if (RandomInt(0, 1)) { const unsigned int width = RandomInt(0, 100); char buf[4]; sprintf(buf, "%u", width); formatSpec += buf; } // Generate an optional random precision if (RandomInt(0, 1)) { const unsigned int prec = RandomInt(0, 100); char buf[4]; sprintf(buf, "%u", prec); formatSpec += '.'; formatSpec += buf; } // Generate a random type character static const string type("cdeEfgGinopsuxX"); const char typeSpec = type[RandomInt(0u, type.size() - 1)]; // Generate an optional type prefix static const string prefix("hl"); if (typeSpec != 's' && RandomInt(0, 1)) { formatSpec += prefix[RandomInt(0u, prefix.size() - 1)]; } formatSpec += typeSpec; formatSpec += '|'; formatSpec += RandomString(100); switch (typeSpec) { case 'c': TestCase(formatSpec, RandomInt(1, 127)); break; case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': //TestCase(formatSpec, RandomInt(-10000, 10000)); // don't test negative values on 64bit systems, because // snprintf does not support 64 Bit values TestCase(formatSpec, RandomInt( -10000 * (sizeof(size_t)>4 ? 0 : 1) , 10000)); break; case 'e': case 'E': case 'f': case 'g': case 'G': TestCase(formatSpec, RandomInt(-10000, 10000) / double(RandomInt(1, 100))); break; case 'n': break; case 'p': { void * p = malloc(RandomInt(1, 1000)); TestCase(formatSpec, p); free(p); } break; case 's': TestCase(formatSpec, RandomString(100).c_str()); break; default: assert(false); break; } } cout << "Finished RandomTest" << endl; } // ---------------------------------------------------------------------------- void * DoLokiFPrintfLoop( void * p ) { const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); for ( unsigned int loop = 0; loop < 10; ++loop ) { ::Loki::FPrintf( "Loop: [%u] Thread: [%u]\n" )( loop )( threadIndex ); } return 0; } // ---------------------------------------------------------------------------- void * DoLokiPrintfLoop( void * p ) { const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); for ( unsigned int loop = 0; loop < 10; ++loop ) { ::Loki::Printf( "Loop: [%u] Thread: [%u]\n" )( loop )( threadIndex ); } return 0; } // ---------------------------------------------------------------------------- void * DoStdOutLoop( void * p ) { const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); for ( unsigned int loop = 0; loop < 10; ++loop ) { printf( "Loop: [%d] Thread: [%d]\n", loop, threadIndex ); } return 0; } // ---------------------------------------------------------------------------- void * DoCoutLoop( void * p ) { const unsigned int threadIndex = reinterpret_cast< unsigned int >( p ); for ( unsigned int loop = 0; loop < 10; ++loop ) { cout << "Loop: [" << loop << "] Thread: [" << threadIndex << "]\n"; } return 0; } // ---------------------------------------------------------------------------- void AtomicTest( void ) { char ender; cout << "Starting Loki::Printf AtomicTest" << endl; { ThreadPool pool; pool.Create( 20, &DoLokiPrintfLoop ); pool.Start(); pool.Join(); } cout << "Finished Loki::Printf AtomicTest." << endl; cout << "If the output lines up in neat columns, the test passed." << endl; cout << "If the output is not in columns, then the test failed." << endl; cout << "Press key to continue. "; cin.get( ender ); cout << "Starting Loki::FPrintf AtomicTest" << endl; { ThreadPool pool; pool.Create( 20, &DoLokiFPrintfLoop ); pool.Start(); pool.Join(); } cout << "Finished Loki::FPrintf AtomicTest." << endl; cout << "If the output lines up in neat columns, the test passed." << endl; cout << "If the output is not in columns, then the test failed." << endl; cout << "Press key to continue. "; cin.get( ender ); cout << "Starting stdout AtomicTest" << endl; { ThreadPool pool; pool.Create( 20, &DoStdOutLoop ); pool.Start(); pool.Join(); } cout << "Finished stdout AtomicTest." << endl; cout << "If the output lines up in neat columns, your compiler implements printf correctly." << endl; cout << "If the output is not in columns, then your compiler implements printf incorrectly." << endl; cout << "Press key to continue. "; cin.get( ender ); cout << "Starting cout AtomicTest" << endl; { ThreadPool pool; pool.Create( 20, &DoCoutLoop ); pool.Start(); pool.Join(); } cout << "Finished cout AtomicTest." << endl; cout << "If the output lines up in neat columns, your compiler implements cout correctly." << endl; cout << "If the output is not in columns, then your compiler implements cout incorrectly." << endl; cout << "Press key to continue. "; cin.get( ender ); } // ---------------------------------------------------------------------------- class CommandLineArgs { public: CommandLineArgs( unsigned int argc, const char * argv[] ); inline unsigned int GetSpeedLoopCount( void ) const { return m_speedLoopCount; } inline unsigned int GetRandomLoopCount( void ) const { return m_randomLoopCount; } inline bool DoSpeedTest( void ) const { return ( 0 < m_speedLoopCount ); } inline bool DoRandomTest( void ) const { return ( 0 < m_randomLoopCount ); } inline bool DoFormatTest( void ) const { return m_doFormatTest; } inline bool DoAtomicTest( void ) const { return m_doAtomicTest; } inline bool DoShowHelp( void ) const { return m_showHelp; } void ShowHelp( void ) const; private: unsigned int m_speedLoopCount; unsigned int m_randomLoopCount; bool m_doFormatTest; bool m_doAtomicTest; bool m_showHelp; const char * m_exeName; }; // ---------------------------------------------------------------------------- CommandLineArgs::CommandLineArgs( unsigned int argc, const char * argv[] ) : m_speedLoopCount( 0 ), m_randomLoopCount( 0 ), m_doFormatTest( false ), m_doAtomicTest( false ), m_showHelp( false ), m_exeName( argv[0] ) { if ( argc < 2 ) { m_showHelp = true; return; } bool isValid = true; for ( unsigned int ii = 1; ( isValid ) && ( ii < argc ); ++ii ) { const char * arg = argv[ii]; const unsigned int length = ::strlen( arg ); if ( ( '-' != *arg ) || ( length < 2 ) ) { isValid = false; break; } switch ( arg[1] ) { default: isValid = false; break; case 'h': m_showHelp = true; break; case 'f': m_doFormatTest = true; break; case 'a': m_doAtomicTest = true; break; case 'r': { if ( ( length < 3 ) || ( ':' != arg[2] ) || ( 0 == isdigit( arg[3] ) ) ) { isValid = false; break; } m_randomLoopCount = ::atoi( arg + 3 ); break; } case 's': { if ( ( length < 3 ) || ( ':' != arg[2] ) || ( 0 == isdigit( arg[3] ) ) ) { isValid = false; break; } m_speedLoopCount = ::atoi( arg + 3 ); break; } } } if ( !isValid ) { m_showHelp = true; m_speedLoopCount = 0; m_randomLoopCount = 0; m_doFormatTest = false; } } // ---------------------------------------------------------------------------- void CommandLineArgs::ShowHelp( void ) const { cout << "Usage: " << m_exeName << " [-h] [-f] [-r:#] [-s:#]" << endl; cout << " -h Show this help info and exit. Overrides all other options." << endl; cout << " -f Run formatting tests." << endl; cout << " -a Run atomic tests." << endl; cout << " -r:# Run random tests for # of loops. # is a positive decimal value greater than 100." << endl; cout << " -s:# Run speed tests for # of loops. # is a positive decimal value greater than 100." << endl; } // ---------------------------------------------------------------------------- int main( int argc, const char * argv[] ) { const CommandLineArgs args( argc, argv ); if ( args.DoShowHelp() ) { args.ShowHelp(); return 0; } if ( args.DoSpeedTest() ) { SpeedTest( args.GetSpeedLoopCount() ); } if ( args.DoRandomTest() ) { RandomTest( args.GetRandomLoopCount() ); } if ( args.DoFormatTest() ) { FormatTest(); } if ( args.DoAtomicTest() ) { AtomicTest(); } } // ----------------------------------------------------------------------------