// ============================================================================= // // C/C++ Program Performance Measurement #define PROGRAM_NAME "Simple C/C++ Perfometer: Splitting string into vector of vectors" #define PROGRAM_VERSION "Version S2VV-1.0" // // ------------------------------------- // // Copyright (C) 2002-2005 Alex Vinokur // // ---------------------------------------------------------------------------- // // This software is provided 'as-is', without any express or implied // warranty. In no event will the author be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // // Alex Vinokur // // ---------------------------------------------------------------------------- // // email:alex DOT vinokur AT gmail DOT com // http://up.to/alexv // // ============================================================================= // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // Testsuites // ---------- // Unix-C-01 : Function strsep() // CPP-01 : std::getline, std::istream_iterator // CPP-02 : istream::::getline, std::istream_iterator // CPP-03 : for-loop, std::find_first_of // // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // -------------------------------------- #if ((defined unix) || (defined __unix) || (defined unix__) || (defined __unix__)) #define UNIX_ENV #endif // ============== #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; // -------------------------------------- typedef unsigned long ulong; typedef unsigned int uint; // -------------------------------------- #define MAX_VALUE(x,y) ((x) > (y) ? (x) : (y)) #define MIN_VALUE(x,y) ((x) < (y) ? (x) : (y)) #define ASSERT(x) if (!(x)) \ { \ assert(x); \ cerr << "[" \ << __FILE__ \ << ", " \ << __LINE__ \ << "] assert() not working" \ << endl; \ abort(); \ } // #define ASSERT2(x) ASSERT(x) #define ASSERT2(x) // ======================================== // ====== Part-1: Common Auxilary Functions // ======================================== // ------------------------------ // Function 1.01 static string get_compiler_info() // ------------------------------ { ostringstream oss; ostringstream tss; string str; // ------ GNU gcc ------ #ifdef __GNUC__ oss.str(""); tss.str(""); str.erase(); oss << "GNU gcc " << __GNUC__; #ifdef __GNUC_MINOR__ oss << "." << __GNUC_MINOR__; #ifdef __GNUC_PATCHLEVEL__ #if (__GNUC_PATCHLEVEL__) oss << "." << __GNUC_PATCHLEVEL__; #endif #endif #endif #if (__CYGWIN32__ || __CYGWIN__) oss << " (CYGWIN)"; #endif #if (__MINGW32__ || __MINGW__ ) oss << " (MINGW)"; #endif #if (__DJGPP__) oss << " (DGGPP " << __DJGPP__; #ifdef __DJGPP_MINOR__ oss << "." << __DJGPP_MINOR__; #endif oss << ")"; #endif #endif // ------ Microsoft C++ ------ #ifdef _MSC_VER oss.str(""); tss.str(""); str.erase(); oss << "Microsoft C++ "; tss << _MSC_VER; str = tss.str(); ASSERT (str.size() == 4); oss << str[0] << str[1] << "." << str[2] << str[3]; #ifdef _MANAGED #if (_MANAGED) oss << " (Managed)"; #else ASSERT (0); oss << " (Unmanaged)"; #endif #else oss << " (Unmanaged)"; #endif #endif // ------ Intel C++ ------ #ifdef __INTEL_COMPILER oss.str(""); tss.str(""); str.erase(); oss << "Intel C++ "; tss << __INTEL_COMPILER; str = tss.str(); ASSERT (str.size() == 3); oss << str[0] << "." << str[1]; #endif // ------ Borland C++ ------ #ifdef __BCPLUSPLUS__ oss.str(""); tss.str(""); str.erase(); oss << "Borland C++ "; tss << hex << __BCPLUSPLUS__; str = tss.str(); ASSERT (str.size() == 3); oss << str[0] << "." << str[1] << "." << str[2]; #endif // ------ Digital Mars C++ ------ #ifdef __DMC__ oss.str(""); tss.str(""); str.erase(); #ifndef __DMC_VERSION_STRING__ #error __DMC_VERSION_STRING__ Not Defined #endif oss << __DMC_VERSION_STRING__; #endif return oss.str(); } // get_compiler_info // ----------------------------- // Function 1.02 static void show_compiler_info() // ----------------------------- { const string str(get_compiler_info()); if (str.empty()) return; cout << string (str.size(), '-') << endl; cout << str << endl; cout << string (str.size(), '-') << endl; } // =================================== // ====== Part-2: Local Data & Defines // =================================== // -------------------------------------- static vector foo_names; static vector::iterator iter_names; static vector > used_time; static ulong sup_run_no = 0; static ulong run_no = 0; static ulong test_no = 0; static size_t user_defined_strsize = 0; static uint foo_setw = 0; #define NEWLINE_CHAR_SYMBOL '\n' #define SPACE_CHAR_SYMBOL ' ' #define TAB_CHAR_SYMBOL '\t' static const int newline_int_symbol (int ('\n')); static const int space_int_symbol (int (' ')); #define WORD_DELIMS " \t" #define LINE_DELIMS "\n" static vector > result_vvect; enum StringType { STR_ONLY_NEWLINES, STR_ONLY_SPACES, STR_INIFORMLY, STR_RANDOMALY, StringType_Total }; // ------------------------------ #define SMART_ASSERT(x) if (!(x)) { cerr << "SOURCE LINENo = " << lineno_i << endl; ASSERT(x); abort(); } #define TRACE(x) // #define TRACE(x) cerr << x << endl #define CHECK_RETURNED_VVECTOR(s,v) check_returned_vvector(s, v, __LINE__) // ------------------------------ // ========================================== // ====== Part-3: Locat Preparation Functions // ========================================== // ----------------------------- // Function 3.00 static string getstr_vvector (const vector >& vvect_i, const string& msg_i) // ----------------------------- { ostringstream oss; if (!msg_i.empty()) oss << "\t=== " << msg_i << " ===" << endl; oss << "--- Sizes: " << endl; for (size_t i = 0; i < vvect_i.size(); i++) { oss << "[" << setw (5) << (i + 1) << "] "; if (vvect_i[i].empty()) oss << 0; else { for (size_t k = 0; k < vvect_i[i].size(); k++) { oss << (vvect_i[i][k].empty() ? 0 : vvect_i[i][k].size()) << " "; } } oss << "\n"; } oss << "--- Content: " << endl; for (size_t i = 0; i < vvect_i.size(); i++) { oss << "[" << setw (5) << (i + 1) << "] "; for (size_t k = 0; k < vvect_i[i].size(); k++) { oss << "<" << vvect_i[i][k] << "> "; } oss << "\n"; } return oss.str(); } // ----------------------------- // Function 3.01 static vector > string_to_vvector ( const string& str_i ) // ----------------------------- { vector > ret_vvect; istringstream iss1 (str_i.c_str()); string line; while (getline(iss1, line)) { istringstream iss2 (line.c_str ()); istream_iterator b(iss2), e; ret_vvect.push_back(vector (b, e)); } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; return ret_vvect; } // ----------------------------- // Function 3.02 static size_t get_total_lines (const string& str_i) // ----------------------------- { vector > vvect (string_to_vvector (str_i)); return vvect.size(); } // ----------------------------- // Function 3.03 static size_t get_total_fields (const string& str_i) // ----------------------------- { vector > vvect (string_to_vvector (str_i)); size_t total_fields = 0; for (size_t i = 0; i < vvect.size(); i++) total_fields += vvect[i].size(); return total_fields; } // ----------------------------- // Function 3.04 static bool vvectors_are_identical ( const vector > vv1_i, const vector > vv2_i ) // ----------------------------- { if (vv1_i.size() != vv2_i.size()) return false; ASSERT (vv1_i.size() == vv2_i.size()); const size_t vv_size (vv1_i.size()); for (size_t k = 0; k < vv_size; k++) { if (vv1_i[k].size() != vv2_i[k].size()) return false; ASSERT (vv1_i[k].size() == vv2_i[k].size()); const size_t vvrow_size (vv1_i[k].size()); for (size_t s = 0; s < vvrow_size; s++) { if (vv1_i[k][s].size() != vv2_i[k][s].size()) return false; if (vv1_i[k][s] != vv2_i[k][s]) return false; } } return true; } // vvectors_are_identical // ----------------------------- // Function 3.05 void check_returned_vvector ( const string& str_i, const vector >& vvect_i, const int lineno_i ) // ----------------------------- { const vector > base_vvector (string_to_vvector (str_i)); if (!vvectors_are_identical (base_vvector, vvect_i)) { cerr << "FATAL ERROR: result vvector and base vvector are not identical" << endl; } SMART_ASSERT (vvectors_are_identical (base_vvector, vvect_i)); } // --------------------------------------- // Function 3.06 static char get_next_graph_char () // --------------------------------------- { #define START_CH 0 static char ch = START_CH; while ((!isgraph(ch++)) || (ch == SCHAR_MAX)) { ch = ch%SCHAR_MAX; } return ch; } // --------------------------------------- // Function 3.07 static char get_next_spec_char () // --------------------------------------- { #define START_CH 0 static char ch = START_CH; while ((!isprint(ch++)) || (ch == SCHAR_MAX)) { ch = ch%SCHAR_MAX; } return ch; } // --------------------------------------- // Function 3.08 static string fill_input_string ( uint string_size_i, StringType stype_i ) // --------------------------------------- { string ret_str; const string::size_type newline_pos = int (sqrt(float(string_size_i))); const string::size_type space_pos = int (sqrt(float(newline_pos))); switch (stype_i) { case STR_ONLY_NEWLINES: ret_str = string (string_size_i, NEWLINE_CHAR_SYMBOL); break; case STR_ONLY_SPACES: ret_str = string (string_size_i, SPACE_CHAR_SYMBOL); break; case STR_INIFORMLY: ret_str = string (string_size_i, 0); for (size_t i = newline_pos; i < string_size_i; i += newline_pos) { ret_str [i] = NEWLINE_CHAR_SYMBOL; } for (size_t i = space_pos; i < string_size_i; i += space_pos) { if (ret_str [i] == 0) { ret_str [i] = SPACE_CHAR_SYMBOL; } } for (size_t i = 0; i < string_size_i; i++) { if (ret_str [i] == 0) { ret_str [i] = get_next_graph_char (); } } break; case STR_RANDOMALY: ret_str = string (string_size_i, 0); for (size_t i = 0; i < string_size_i; i++) { char ch; bool flag = true; do { ch = rand()/SCHAR_MAX; flag = !(isprint (ch) || (ch == NEWLINE_CHAR_SYMBOL) || (ch == TAB_CHAR_SYMBOL)); } while (flag); // while (!isprint(ret_str [i] = rand()/SCHAR_MAX)); ret_str [i] = ch; } break; default: assert (0); break; } return ret_str; } // --------------------------------------- // Function 3.09 static vector fill_all_input_strings ( uint string_size_i ) // --------------------------------------- { vector ret_vect; for (int i = 0; i < StringType_Total; i++) { ret_vect.push_back (fill_input_string(string_size_i, static_cast (i))); } return ret_vect; } #ifdef UNIX_ENV // -------------------------------------- // Function S.01 vector split_str_via_strsep (const string& str_i, const char * const delims_i) // --------------------------------------- { vector ret_vect; if (str_i.empty()) return ret_vect; const size_t str_size (str_i.size()); char* tmp_str = new char [str_size + 1]; strcpy (tmp_str, str_i.c_str()); if (strchr (delims_i, tmp_str[str_size - 1])) tmp_str[str_size - 1] = 0; char *token; while ((token = strsep (&tmp_str, delims_i))) { ret_vect.push_back (token); } delete [] tmp_str; return ret_vect; } #endif // -------------------------------------- // Function S.02 vector split_str_via_find_first (const string& str_i, const char * const delims_i) // -------------------------------------- { vector ret_vect; if (str_i.empty()) return ret_vect; const string::size_type str_size = str_i.size(); string::size_type pos1 = 0; while (pos1 < str_size) { string::size_type pos2 = str_i.find_first_of(delims_i, pos1); if (pos2 == string::npos) pos2 = str_size; ret_vect.push_back(str_i.substr(pos1, pos2 - pos1)); pos1 = pos2 + 1; } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; return ret_vect; } // ==================================== // ====== Part-4: Function To Be Tested // ==================================== // -------------------------------------- // -------------------------------------- // -------------------------------------- #ifdef UNIX_ENV vector > Unix_C_01__strsep (const string& str_i) { vector > ret_vvect; vector line_vect = split_str_via_strsep (str_i, LINE_DELIMS); for (size_t i = 0; i < line_vect.size(); i++) { vector tmp_vect (split_str_via_strsep (line_vect[i], WORD_DELIMS)); tmp_vect.erase(remove(tmp_vect.begin(), tmp_vect.end(), string()), tmp_vect.end()); ret_vvect.push_back (tmp_vect); } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; return ret_vvect; } #endif // -------------------------------------- vector > CPP_01__function_getline__istream_iterator (const string& str_i) { vector > ret_vvect; istringstream iss1 (str_i.c_str()); string line; while (getline(iss1, line)) { istringstream iss2 (line.c_str ()); istream_iterator b(iss2), e; ret_vvect.push_back(vector (b, e)); } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; return ret_vvect; } // -------------------------------------- vector > CPP_02__method_getline__istream_iterator (const string& str_i) { vector > ret_vvect; istringstream iss1 (str_i.c_str()); const size_t str_size (str_i.size() + 1); char* buffer = new char[str_size]; while (iss1.getline (buffer, str_size, NEWLINE_CHAR_SYMBOL)) { istringstream iss2 (buffer); istream_iterator b(iss2), e; ret_vvect.push_back(vector (b, e)); } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; delete [] buffer; return ret_vvect; } // -------------------------------------- vector > CPP_03__loop__find_first (const string& str_i) { vector > ret_vvect; vector line = split_str_via_find_first (str_i, LINE_DELIMS); for (size_t i = 0; i < line.size(); i++) { vector tmp_vect (split_str_via_find_first (line[i], WORD_DELIMS)); tmp_vect.erase(remove(tmp_vect.begin(), tmp_vect.end(), string()), tmp_vect.end()); ret_vvect.push_back (tmp_vect); } // cout << getstr_vvector (ret_vvect, __FUNCTION__) << endl; return ret_vvect; } // ===================================== // ====== Part-5: Computation Management // ===================================== // ----------------------------------- // ----------------------------------- // ----------------------------------- #define MEASURE_IT(x, y, z) \ foo_setw = MAX_VALUE (foo_setw, string (#y).size()); \ cerr << "[Run-" << char ('A' + sup_run_no - 1) << "." << run_no << "] Test-" << test_no << ": " << #y << endl; \ start_time = clock(); \ ASSERT (start_time != clock_t (-1)); \ { for (ulong k = 0; k < no_of_repetitions; k++) { result_vvect = x; } } \ end_time = clock(); \ ASSERT (end_time != clock_t (-1)); \ ASSERT2 (vvectors_are_identical (result_vvect, string_to_vvector (z))); \ if (!(end_time > start_time)) { cerr << "Number of repetitions too small: " << #y << endl; }\ ASSERT (end_time >= start_time); \ if (find (foo_names.begin(), foo_names.end(), #y) == foo_names.end()) \ { \ foo_names.push_back (#y); \ used_time.push_back (vector()); \ } \ ASSERT (foo_names.size() == used_time.size()); \ iter_names = find (foo_names.begin(), foo_names.end(), #y); \ ASSERT (iter_names != foo_names.end()); \ used_time[distance (foo_names.begin(), iter_names)].push_back ((end_time - start_time)) #define MEASURE_WITH_ARG(foo, argument) MEASURE_IT (foo(argument), foo, argument) // #define MEASURE_WITH_NO_ARG(foo) MEASURE_IT (foo(), foo) #define MEASURE_S2V_WITH_ARG(foo) MEASURE_WITH_ARG (foo, input_string) #define CHECK_S2V_RETURNED_VVECTOR CHECK_RETURNED_VVECTOR(input_string, result_vvect) // ------------ void measure (ulong no_of_repetitions, const string& input_string) { clock_t start_time; clock_t end_time; vector elapsed_time_vect; // ------------------------------- // ------------------------------- // cout << "\t---> Test-" << test_no << " started" << endl; cout << "."; cout.flush(); // ------------------------------- #ifdef UNIX_ENV MEASURE_S2V_WITH_ARG (Unix_C_01__strsep); CHECK_S2V_RETURNED_VVECTOR; #endif MEASURE_S2V_WITH_ARG (CPP_01__function_getline__istream_iterator); CHECK_S2V_RETURNED_VVECTOR; MEASURE_S2V_WITH_ARG (CPP_02__method_getline__istream_iterator); CHECK_S2V_RETURNED_VVECTOR; MEASURE_S2V_WITH_ARG (CPP_03__loop__find_first); CHECK_S2V_RETURNED_VVECTOR; // ------------------------------- // cerr << "\t Test-" << test_no << " finished" << endl; // ------------------------------- } // ------------ void show (ulong no_of_tests) { clock_t units; clock_t sum; #define THRESHOLD 0.2 const ulong threshold = ulong(no_of_tests * THRESHOLD); ASSERT ((threshold * 2) <= no_of_tests); cout << endl; ASSERT (foo_names.size() == used_time.size()); for (ulong i = 0; i < foo_names.size(); i++) { sum = 0; ASSERT (no_of_tests == used_time[i].size()); for (ulong k = threshold; k < (used_time[i].size() - threshold); k++) { sum += used_time[i][k]; } units = sum/(used_time[i].size() - (threshold * 2)); cout << setw(foo_setw) << std::left << foo_names[i] << " : " << setw(6) << std::right << units << " units" << " ("; cout.setf(ios::fixed, ios::floatfield); cout << setprecision (3) << (float(units)/float(CLOCKS_PER_SEC)) << " secs)" << endl; } } // ------------ void run (ulong no_of_runs, ulong no_of_tests, ulong no_of_repetitions) { const vector input_strings (fill_all_input_strings (user_defined_strsize)); cout << endl; for (ulong t = 0; t < input_strings.size(); t++) { const size_t total_lines (get_total_lines (input_strings[t])); const size_t total_fields (get_total_fields (input_strings[t])); cout << "\t=== String-" << char ('A' + t) << ": " << setw (7) << total_lines << " line" << ((total_lines == 1) ? ", " : "s,") << setw (9) << total_fields << " field" << ((total_fields == 1) ? "" : "s") << endl; } cout << endl; for (ulong t = 0; t < input_strings.size(); t++) { sup_run_no++; run_no = 0; for (ulong i = 0; i < no_of_runs; i++) { run_no = i + 1; foo_names.clear(); used_time.clear(); ostringstream oss; // ---------------------- oss << "" << " Run-" // << sup_run_no << char ('A' + sup_run_no - 1) << "." << run_no << " of " // << input_strings.size() << char ('A' + sup_run_no - 1) << "." << no_of_runs << " : "; cout << endl; cout << endl; cout << oss.str() << "Started "; cout.flush(); for (ulong k = 0; k < no_of_tests; k++) { test_no = k + 1; measure (no_of_repetitions, input_strings[t]); } show (no_of_tests); cout << oss.str() << "Finished "; } } } // ------------ int main(int argc, char** argv) { cout << endl; cout << string (string (PROGRAM_NAME).size(), '=') << endl; cout << PROGRAM_NAME << endl; cout << PROGRAM_VERSION << endl; cout << string (string (PROGRAM_NAME).size(), '=') << endl; // -------------------------- cout << endl; cout << endl; show_compiler_info(); cout << endl; cout << "\tYOUR COMMAND LINE : "; string exe_name (argv[0]); cout << exe_name.substr (exe_name.find_last_of ("/\\") + 1) << " "; for (long i = 1; i < argc; i++) cout << argv[i] << " "; cout << endl; cout << endl; if (!(argc >= 4)) { cout << "\tUSAGE : " << argv[0] << " " << " []" << endl; return 1; } ASSERT (argc >= 4); user_defined_strsize = atoi (argv[1]); ASSERT (user_defined_strsize > 0); const ulong no_of_tests (atoi (argv[2])); ASSERT (no_of_tests > 0); const ulong no_of_repetitions (atoi (argv[3])); ASSERT (no_of_repetitions > 0); const ulong no_of_runs (((argc > 4) ? atoi (argv[4]) : 1)); ASSERT (no_of_runs > 0); cout << "\t### String size : " << user_defined_strsize << endl; cout << "\t### Number of runs : " << no_of_runs << endl; cout << "\t### Number of tests : " << no_of_tests << endl; cout << "\t### Number of repetitions : " << no_of_repetitions << endl; cout << "\t### CLOCKS_PER_SEC : " << CLOCKS_PER_SEC << endl; cout << endl; // ----------------------------- run (no_of_runs, no_of_tests, no_of_repetitions); return 0; }