#include #include #include #include #include #include #include #include "benchmark.h" FactoriesMapHolder & FactoriesMapHolder::getInstance() { static FactoriesMapHolder instance; return instance; } FactoriesMapHolder::~FactoriesMapHolder() { // deallocate all factories registered in benchFactoryMap // (this includes factories from benchThrashingFactoryMap as well) for (std::map::iterator it = benchFactoryMap.begin(); it != benchFactoryMap.end(); it++) { delete it->second; } } THRASHINGBENCHFACTORY("none", DummyThrasher) using namespace std; Benchmark::Benchmark() { assert(false); } Benchmark::Benchmark(string _benchmark_name) : benchmark_name(_benchmark_name), state(STATE_UNSET) { papi_sets_cnt = 1; #ifdef ENABLE_PAPI papi_counters = 0; papi_sets = NULL; papi_event_names.clear(); papi_event_codes = NULL; papi_events_cnt = 0; papi_events_order = NULL; papi_values_start = NULL; papi_values = NULL; #endif } Benchmark::~Benchmark() { #ifdef ENABLE_PAPI if (papi_sets) { for (unsigned i = 0; i < papi_sets_cnt; i++) { PAPI_cleanup_eventset(papi_sets[i]); PAPI_destroy_eventset(&papi_sets[i]); } delete[] papi_sets; papi_sets = NULL; papi_sets_cnt = 0; } if (papi_event_codes) { delete[] papi_event_codes; papi_event_codes = NULL; } if (papi_events_order) { delete[] papi_events_order; papi_events_order = NULL; } if (papi_values_start) { delete[] papi_values_start; papi_values_start = NULL; } if (papi_values) { delete[] papi_values; papi_values = NULL; } #endif } void Benchmark::initialize_params (std::map & iniParams, bool plot, bool papi) { params.initialize(iniParams, plot); #ifdef ENABLE_PAPI if (papi) { map::iterator it = iniParams.find("papi_events"); if (it != iniParams.end()) { const params_list &_papi_events = it->second; papi_events_cnt = _papi_events.size(); papi_event_names.reserve(papi_events_cnt); papi_event_codes = new int[papi_events_cnt]; int * event = papi_event_codes; for (params_list::const_iterator itt = _papi_events.begin(); itt != _papi_events.end(); itt++, event++) { BenchParameterString * const papiEventsParam = (BenchParameterString *)*itt; string evname = papiEventsParam->value; papi_event_names.push_back(evname); int r; if ((r = PAPI_event_name_to_code((char*)evname.c_str(), event)) != PAPI_OK) { cerr << "PAPI_event_name_to_code(" << evname << "): " << PAPI_strerror(r) << endl; if (!plot) exit(EXIT_FAILURE); } // delete the further unused BenchParameter delete papiEventsParam; } // remove papi_events from the iniParams list iniParams.erase(it); if (!plot) { map::iterator it2 = iniParams.find("papi_counters"); if (it2 != iniParams.end()) { params_list & papiCountersParams = it2->second; // there should be only one number for the papi_counters param assert(papiCountersParams.size() == 1); // get the first (and only) param in the list params_list::iterator it3 = papiCountersParams.begin(); BenchParameterNumeric * const papiCountersParam = (BenchParameterNumeric*) *it3; // current value = first value papiCountersParam->reset(); // there should be no range of values assert(!papiCountersParam->nextStepPossible()); papi_init(papiCountersParam->current); // the param is no longer used delete papiCountersParam; // remove the pointer from the list as well iniParams.erase(it2); } else { papi_init(0); } } } } // end if (papi) #endif // clean the rest of unused parameters for (std::map::iterator itt = iniParams.begin(); itt != iniParams.end(); itt++) { params_list & paramsToDelete = itt->second; for (params_list::iterator it2 = paramsToDelete.begin(); it2 != paramsToDelete.end(); it2++) { delete *it2; } paramsToDelete.clear(); } iniParams.clear(); } unsigned Benchmark::papi_sets_count() { return papi_sets_cnt; } #ifdef ENABLE_PAPI void Benchmark::papi_init(int _papi_counters) { if (papi_sets) { for (unsigned i = 0; i < papi_sets_cnt; i++) { PAPI_cleanup_eventset(papi_sets[i]); PAPI_destroy_eventset(&papi_sets[i]); } delete[] papi_sets; papi_sets = NULL; papi_sets_cnt = 0; } if (papi_events_order) { delete[] papi_events_order; papi_events_order = NULL; } if (papi_values_start) { delete[] papi_values_start; papi_values_start = NULL; } if (papi_values) { delete[] papi_values; papi_values = NULL; } if (PAPI_is_initialized() == PAPI_NOT_INITED) { cerr << PAPI_NOT_INITED << endl; exit(EXIT_FAILURE); } if (PAPI_num_hwctrs() < _papi_counters) { cerr << "papi_counters value specified (" << _papi_counters << ") exceeds the number of HW counters (" << PAPI_num_hwctrs() << ")" << endl; exit(EXIT_FAILURE); } // initialize papi_counters as specified or number of HW counters otherwise papi_counters = _papi_counters != 0 ? _papi_counters : PAPI_num_hwctrs(); // no need to have more counters than events if (papi_counters > papi_events_cnt) { papi_counters = papi_events_cnt; } // determine number of sets needed papi_sets_cnt = (papi_events_cnt + papi_counters - 1) / papi_counters; // at least one so that for cycles work if (papi_sets_cnt == 0) { papi_sets_cnt = 1; } // create storage for the event sets papi_sets = new int[papi_sets_cnt]; // create array of event numbers to access events and result columns papi_events_order = new unsigned[papi_events_cnt]; // fill it for (unsigned i = 0; i < (unsigned) papi_events_cnt; i++) { papi_events_order[i] = i; } // and shuffle it random_shuffle(papi_events_order, papi_events_order + papi_events_cnt); int r; // create the events sets and fill them with randomized events // all sets will have #papi_counters events, last set will be filled up from beginning if needed for (unsigned i = 0; i < papi_sets_cnt; i++) { papi_sets[i] = PAPI_NULL; if ((r = PAPI_create_eventset(&papi_sets[i])) != PAPI_OK) { cerr << "PAPI_create_eventset: " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } cout << "Filling set number " << i << endl; for (int j = 0; j < papi_counters; j++) { // determine the event's position in papi_events; unsigned pos = papi_events_order[(i * papi_counters + j) % papi_events_cnt]; string s = papi_event_names[pos]; int event = papi_event_codes[pos]; // PAPI_event_info_t event_info; // if ((r = PAPI_get_event_info(event, &event_info)) != PAPI_OK) { // cerr << "Error getting event info for event " << s << ": " << PAPI_strerror(r) << endl; // exit(EXIT_FAILURE); // } cout << "PAPI_add_event(" << s << ") code " << hex << showbase << (unsigned) event << noshowbase << dec << endl; // cout << "Short descr: " << event_info.short_descr << endl; // cout << "Long descr: " << event_info.long_descr << endl; if ((r = PAPI_add_event(papi_sets[i], event)) != PAPI_OK) { cerr << "PAPI_add_event(" << s << "): " << PAPI_strerror(r) << endl; if (r == PAPI_ECNFLCT) { // try to determine what conflicts, assuming it's only one event for (int k = 0; k < j; k++) { pos = papi_events_order[(i * papi_counters + k) % papi_events_cnt]; // remove event if ((r = PAPI_remove_event(papi_sets[i], papi_event_codes[pos])) != PAPI_OK) { cerr << "PAPI_remove_event(" << papi_event_names[pos] << "): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } // try to add our event again if ((r = PAPI_add_event(papi_sets[i], event)) == PAPI_OK) { cerr << "Conflicting event: " << papi_event_names[pos] << endl; // and remove to test others for conflict if ((r = PAPI_remove_event(papi_sets[i],event)) != PAPI_OK) { cerr << "PAPI_remove_event(" << s << "): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } } else if (r != PAPI_ECNFLCT) { // failed for other reason cerr << "PAPI_add_event(" << s << "): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } // readd the removed event if ((r = PAPI_add_event(papi_sets[i], papi_event_codes[pos])) != PAPI_OK) { cerr << "PAPI_add_event(" << papi_event_names[pos] << "): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } } } exit(EXIT_FAILURE); } } } cout << "PAPI events total: " << papi_events_cnt << endl; papi_values_start = new long_long[papi_events_cnt]; papi_values = new long_long[papi_events_cnt]; } void Benchmark::papi_start_counting(unsigned set) { if (papi_events_cnt) { int r; if ((r = PAPI_start(papi_sets[set])) != PAPI_OK) { cerr << "PAPI_start: " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } } } void Benchmark::papi_stop_counting(unsigned set) { if (papi_events_cnt) { int r; if ((r = PAPI_stop(papi_sets[set], papi_values)) != PAPI_OK) { cerr << "PAPI_stop): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } } } void Benchmark::papi_start(unsigned set) { if (papi_events_cnt) { int r; if ((r = PAPI_read(papi_sets[set], papi_values_start)) != PAPI_OK) { cerr << "PAPI_read (start): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } } } void Benchmark::papi_stop(unsigned set, tsc_t * res, tsc_t clocks) { if (papi_events_cnt) { int r; if ((r = PAPI_read(papi_sets[set], papi_values)) != PAPI_OK) { cerr << "PAPI_read (stop): " << PAPI_strerror(r) << endl; exit(EXIT_FAILURE); } // if we measured the first set, store also the clocks as first result if (set == 0) { *res = clocks; } // skip the first result which is clocks res++; for (int i = 0; i < papi_counters; i++) { // don't record results of counters that are filled up from beginning... it's already counted in lower set int off = set * papi_counters + i; if (off < papi_events_cnt) { res[papi_events_order[off]] = papi_values[i] - papi_values_start[i]; } } } } std::list Benchmark::get_papi_result_names() { list res; // add CPU_Clocks_PAPI if needed if (papi_events_cnt > 0) { column_name clocks_papi("CPU_Clocks_PAPI", "CPU_Clocks_PAPI", "CPU clocks with PAPI sampling"); res.push_back(clocks_papi); } for (vector::iterator it = papi_event_names.begin(); it != papi_event_names.end(); it++) { string resname = *it; string evname = *it; // replace : to . because R does it to column names read from file size_t pos = resname.find_first_of(':'); while (pos != string::npos) { resname[pos] = '.'; pos = resname.find_first_of(':', pos + 1); } column_name col(resname, evname, evname + " counter [events]"); res.push_back(col); } return res; } int Benchmark::get_papi_result_count() { if (papi_events_cnt > 0) { return papi_events_cnt + 1; } else { return 0; } } #endif string Benchmark::result_filename() { ostringstream s; s << benchmark_name; params.printValues(s, ","); return s.str(); } //void //Benchmark::print_result_cols_header(std::ostream &s, std::string prefix) //{ // bool first = true; // // for (int i = 0; i < param_count(); i++) { // if (!first) { // s << "\t"; // } else { // first = false; // } // s << prefix; // s << get_param_name(i); // } //} void Benchmark::print_result_cols_header(std::ostream &s, std::string prefix) { list res = get_result_columns(); bool first = true; for (list::iterator it = res.begin(); it != res.end(); it++) { if (first) { first = false; } else { s << "\t"; } s << prefix; s << it->resultHeader; } } string Benchmark::table_filename(int depth) { ostringstream s; params.printValues(s, ",", false, depth); return s.str(); } bool Benchmark::nextRun() { if (!advance_params(-1)) { return false; } params.resetAll(); init(); return true; } bool Benchmark::advance_params(int skip_depth1, int skip_depth2) { return params.advance(skip_depth1, skip_depth2); } Benchmark * Benchmark::CreateBenchmark(std::string btype) { map & factoryMap = FactoriesMapHolder::getInstance().getBenchFactoryMap(); map::iterator it = factoryMap.find(btype); if (it == factoryMap.end()) { return NULL; } return it->second->newBenchmark(); } ThrashingBenchmark * ThrashingBenchmark::CreateThrashingBenchmark(std::string btype) { map & factoryMap = FactoriesMapHolder::getInstance().getThrashingBenchFactoryMap(); map::iterator it = factoryMap.find(btype); if (it == factoryMap.end()) { return NULL; } return it->second->newThrashingBenchmark(); }