/* * Copyright (c) 2001-2008 * DecisionSoft Limited. All rights reserved. * Copyright (c) 2004-2008 * Oracle. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: xmark.cpp 518 2008-03-07 17:46:47Z jpcs $ */ #ifdef _MSC_VER #pragma warning(disable: 4786) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(XERCES_HAS_CPP_NAMESPACE) XERCES_CPP_NAMESPACE_USE #endif using namespace std; #define MAXIMUM_TIME_FOR_QUERIES 0.2 #define MILLISECS_IN_SECS 1000 Sequence query(XQilla &xqilla, DynamicContext *context, const Item::Ptr &ci, string query) { AutoDelete pquery(xqilla.parse(X(query.c_str()), context, 0, XQilla::NO_ADOPT_CONTEXT)); context->setContextItem(ci); return pquery->execute(context)->toSequence(context); } string timestamp(string &dateTime) { time_t tt; time(&tt); struct tm *tm_p; #ifdef _MSC_VER tm_p = localtime(&tt); #else struct tm tm; tm_p = &tm; localtime_r(&tt, &tm); #endif char szDate[256]; sprintf(szDate,"%04d-%02d-%02dT%02d:%02d:%02dZ", tm_p->tm_year+1900, tm_p->tm_mon+1, tm_p->tm_mday, tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec); dateTime = szDate; sprintf(szDate,"%04d%02d%02d%02d%02d%02d", tm_p->tm_year+1900, tm_p->tm_mon+1, tm_p->tm_mday, tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec); return szDate; } class Timer { public: Timer() { reset(); } void reset() { start_ = 0; duration_ = 0; } void start() { start_ = getTime(); } void stop() { unsigned long end = getTime(); duration_ += end - start_; } double durationInSeconds() const { return ((double)duration_ / MILLISECS_IN_SECS); } private: static unsigned long getTime() { return XMLPlatformUtils::getCurrentMillis(); } unsigned long start_; unsigned long duration_; }; class Stats { public: string name; string data; string size; map info; int count; Timer timer; Stats() { name.clear(); data.clear(); size.clear(); reset(); } void reset() { info.clear(); count = 0; timer.reset(); } }; class StatsReporter { public: StatsReporter(string name, bool verbose) : verbose_(verbose), totalTime_(0), textCentricTime_(0), dataCentricTime_(0), multipleDocumentTime_(0), singleDocumentTime_(0), file_((name + timestamp(timestamp_) + ".xml").c_str()) { if(!file_.is_open()) { cerr << "Unable to open statistics file: " << name << timestamp_ << ".xml" << endl; exit(-1); } file_ << "" << endl; if(verbose_) { cout << "*********************************************************************" << endl; cout << "Benchmark Statistics" << endl << endl; ::snprintf(buffer_, 255, " %5s | %5s | %9s | %10s | %5s | %10s ", "Name", "Data", "Size", "Time/s", "Count", "Ops/s"); cout << buffer_ << endl; cout << "-------+-------+-----------+------------+-------+------------" << endl; } } ~StatsReporter() { file_ << "" << endl; file_.close(); if(verbose_) { cout << endl; cout << "Text Centric Total time/s: " << textCentricTime_ << endl; cout << "Data Centric Total time/s: " << dataCentricTime_ << endl; cout << endl; cout << "Multiple Document Total time/s: " << multipleDocumentTime_ << endl; cout << "Single Document Total time/s: " << singleDocumentTime_ << endl; cout << endl; cout << "Total time/s: " << totalTime_ << endl; cout << "*********************************************************************" << endl; } } void reportStats(const Stats &stats) { file_ << " " << endl; file_ << " " << stats.data << "" << endl; file_ << " " << stats.size << "" << endl; for(map::const_iterator i = stats.info.begin(); i != stats.info.end(); ++i) { file_ << " <" << i->first << ">" << i->second << "first << ">" << endl; } file_ << " " << stats.count << "" << endl; file_ << " " << endl; file_ << " " << endl; if(verbose_) { ::snprintf(buffer_, 255, " %5s | %5s | %9s | %10f | %5i | %10f ", stats.name.c_str(), stats.data.c_str(), stats.size.c_str(), stats.timer.durationInSeconds(), stats.count, ((double)stats.count) / stats.timer.durationInSeconds()); cout << buffer_ << endl; totalTime_ += stats.timer.durationInSeconds(); if(stats.data.find("TC") != string::npos) { textCentricTime_ += stats.timer.durationInSeconds(); } else { dataCentricTime_ += stats.timer.durationInSeconds(); } if(stats.data.find("MD") != string::npos) { multipleDocumentTime_ += stats.timer.durationInSeconds(); } else { singleDocumentTime_ += stats.timer.durationInSeconds(); } } } private: bool verbose_; char buffer_[255]; double totalTime_; double textCentricTime_; double dataCentricTime_; double multipleDocumentTime_; double singleDocumentTime_; string timestamp_; ofstream file_; }; class XMarkResolver : public URIResolver { public: vector defaultCollection; virtual bool resolveDocument(Sequence &result, const XMLCh* uri, DynamicContext* context, const QueryPathNode *projection) { return false; } virtual bool resolveCollection(Sequence &result, const XMLCh* uri, DynamicContext* context, const QueryPathNode *projection) { return false; } virtual bool resolveDefaultCollection(Sequence &result, DynamicContext* context, const QueryPathNode *projection) { for(vector::iterator s = defaultCollection.begin(); s != defaultCollection.end(); ++s) { result.joinSequence(context->resolveDocument(X(s->c_str()), 0, projection)); } return true; } virtual bool putDocument(const Node::Ptr &document, const XMLCh *uri, DynamicContext *context) { return false; } }; void queryBenchmarkData(XQilla &xqilla, DynamicContext *config_context, const Item::Ptr &config, string &dataPath, XQillaConfiguration *conf, Stats &stats, StatsReporter &reporter) { // Find the files XMarkResolver resolver; Result files = query(xqilla, config_context, config, "for $a in /benchmark_data/data_type[@name = '" + stats.data + "']/size[@name = '" + stats.size + "']/file/@name return data($a)"); Item::Ptr file; while((file = files->next(config_context)).notNull()) { string fullpath = dataPath + stats.data + "/" + stats.size + "/" + UTF8(file->asString(config_context)); resolver.defaultCollection.push_back(fullpath); } // Find the queries Result queries = query(xqilla, config_context, config, "for $a at $pos in /benchmark_data/data_type[@name = '" + stats.data + "']/query return (data($a/description), data($a/action), $pos)"); Item::Ptr query; while((query = queries->next(config_context)).notNull()) { stats.reset(); stats.info["description"] = UTF8(query->asString(config_context)); query = queries->next(config_context); string action = UTF8(query->asString(config_context)); query = queries->next(config_context); stats.info["query_index"] = UTF8(query->asString(config_context)); // Replace "input()" with the correct "collection()" function call string::size_type pos = action.find("input()"); while(pos != string::npos) { action = action.replace(pos, 7, "collection()"); pos = action.find("input()"); } stats.info["action"] = ""; try { AutoDelete context(xqilla.createContext(XQilla::XQUERY, conf)); context->registerURIResolver(&resolver, /*adopt*/false); AutoDelete query(xqilla.parse(X(action.c_str()), context, 0, XQilla::NO_ADOPT_CONTEXT)); // Run the query for real, until the total duration is at least MAXIMUM_TIME_FOR_QUERIES while(stats.timer.durationInSeconds() < MAXIMUM_TIME_FOR_QUERIES) { stats.timer.start(); Result result = query->execute(context.get()); Item::Ptr item; while((item = result->next(context.get())).notNull()) { } stats.timer.stop(); ++stats.count; } } catch(XQException& e) { cerr << UTF8(e.getError()) << endl; cerr << "at " << UTF8(e.getXQueryFile()) << ":" << e.getXQueryLine() << ":" << e.getXQueryColumn() << endl; cerr << "at " << e.getCppFile() << ":" << e.getCppLine() << endl; exit(-1); } catch(DOMException &de) { cerr << "DOMException: " << UTF8(de.getMessage()) << endl; exit(-1); } catch(...) { cerr << "[Unknown exception]"; exit(-1); } stats.name = "query"; reporter.reportStats(stats); } } void usage(const char *progname) { const char *name = progname; while(*progname != 0) { if(*progname == '/' || *progname == '\\') { ++progname; name = progname; } else { ++progname; } } cerr << "Usage: " << name << " [options] benchmark_config_file" << endl << endl; cerr << "-h : Show this display" << endl; cerr << "-x : Use the Xerces-C data model (default is the FastXDM)" << endl; } int main(int argc, char *argv[]) { string configPath; string dataPath; XercesConfiguration xercesConf; FastXDMConfiguration fastConf; XQillaConfiguration *conf = &fastConf; for(int i = 1; i < argc; ++i) { if(*argv[i] == '-' && argv[i][2] == '\0' ){ switch(argv[i][1]) { case 'h': { usage(argv[0]); return 0; } case 'x': { conf = &xercesConf; break; } default: { cerr << "Unknown option: " << argv[i] << endl; usage(argv[0]); return 1; } } } else if(configPath.empty()) { configPath = argv[i]; string::size_type pos = configPath.rfind("/"); if(pos != string::npos) { dataPath = configPath.substr(0, pos + 1); } } else { cerr << "Too many parameters" << endl; usage(argv[0]); return 1; } } if(configPath.empty()) { cerr << "Path to the benchmark configuration not specified"<< endl; usage(argv[0]); return 1; } XQilla xqilla; AutoDelete context(xqilla.createContext()); Sequence seq = context->resolveDocument(X(configPath.c_str())); if(seq.isEmpty()) { cerr << "Benchmark configuration not found"<< endl; usage(argv[0]); return 1; } Item::Ptr config = seq.first(); Sequence data_types = query(xqilla, context, config, "distinct-values(/benchmark_data/data_type/@name)"); Sequence size_types = query(xqilla, context, config, "distinct-values(/benchmark_data/data_type/size/@name)"); StatsReporter reporter("statistics", /*verbose*/true); Stats stats; Item::Ptr data; Result data_result = data_types; while((data = data_result->next(context)).notNull()) { stats.data = UTF8(data->asString(context)); Item::Ptr size; Result size_result = size_types; while((size = size_result->next(context)).notNull()) { stats.size = UTF8(size->asString(context)); queryBenchmarkData(xqilla, context, config, dataPath, conf, stats, reporter); } } return 0; }