size_t ATMSP<T>::parse(ATMSB<T> &bc, const std::string &exps, const std::string &vars) { // First always force recursion break on errors size_t eLevel = noErr; if ( (eLevel=setjmp(errJmp)) != noErr ) return eLevel; // Prepare clean expression and variable strings std::string::size_type pos, lastPos; std::string es(exps), vs(vars); pos = 0; while ( (pos=es.find(' '), pos) != std::string::npos ) es.erase(pos, 1); pos = 0; while ( (pos=vs.find(' '), pos) != std::string::npos ) vs.erase(pos, 1); if ( es.empty() ) longjmp(errJmp, funErr); cp = (char *) es.c_str(); // Split comma separated variables into varLst // One instance can be parsed repeatedly. So clear() is vital here varLst.clear(); pos = vs.find_first_of(',', lastPos = vs.find_first_not_of(',', 0)); while ( std::string::npos != pos || std::string::npos != lastPos ) { if ( !varLst.push(vs.substr(lastPos, pos-lastPos)) ) longjmp(errJmp, memErr); pos = vs.find_first_of(',', lastPos = vs.find_first_not_of(',', pos)); } // Static parenthesis check. "Abuse" free opCnt/varCnt as open/close-counters opCnt = varCnt = 0; for (size_t i=0; i<es.size(); i++) if ( es[i] == '(' ) opCnt++; else if ( es[i] == ')' ) { varCnt++; if ( varCnt > opCnt ) longjmp(errJmp, parErr); } if ( opCnt != varCnt ) longjmp(errJmp, parErr); // Reset all our counters and indices // opCnt = Operator count. For bytecode and memory checks // varCnt = Variable count. For check if we have a constant expression // valInd = All num, var and con values are mapped into the bytecode-val-array // numInd = Numerical numbers array index opCnt = varCnt = valInd = numInd = 0; // Run it once for parsing and generating the bytecode expression(bc); bc.opCnt = opCnt; // No vars in expression? Evaluate at compile time then if ( !varCnt ) { bc.num[0] = bc.run(); if ( bc.fltErr ) longjmp(errJmp, nanErr); bc.val[0] = &bc.num[0]; bc.fun[0] = &ATMSB<T>::ppush; bc.opCnt = 1; } return noErr; }
/** Benchmark **/ static void bench(const size_t loops) { double sec, minSec; PTYPE res; // Parser/bytecode instances ATMSP<PTYPE> pa; ATMSB<PTYPE> bc; // Sweep over all expressions std::cout << "Each expression is evaluated " << loops << " x times:\n"; for (size_t i=0; i<list.size(); i++) { // Error check for parsing/bytecode generation size_t err; if ( (err=pa.parse(bc, list[i], "x, y, z")) ) { std::cerr << list[i] << " failed: " << pa.errMessage(err) << std::endl; exit(err); } // Some settings out of inner loops bc.var[0] = 1.0; bc.var[1] = 2.0; bc.var[2] = 3.0; minSec = 10.0e10; // Get fastest run out of a few for (size_t j=0; j<10; j++) { TIMERS t; for (size_t k=0; k<loops; k++) res = bc.run(); sec = t.stop(); if ( sec < minSec ) minSec = sec; } // Set locale for better import in OO spredsheet std::locale locale(""); std::cout.imbue(locale); // Use ";" as delimiter for OO import std::cout << minSec << "\t;\'" << list[i] << std::endl; } }
/// A simple test function. Defines variables x,y,z as x=1, y=2, z=3 always static void test(const char *s, size_t rounds=0) { // Bytecode instance with SAME basic type as the parser ATMSB<PTYPE> byteCode; // Parsing/bytecode generation with error check. In a scope here JUST to // demonstrate that the parser-instance itself is NOT needed later on { ATMSP<PTYPE> parser; size_t err = parser.parse(byteCode, s, "x, y, z"); if ( err ) { std::cerr << parser.errMessage(err) << std::endl; exit(err); } } // Set variable values for x,y,z. Here always 1,2,3 byteCode.var[0] = 1.0; byteCode.var[1] = 2.0; byteCode.var[2] = 3.0; // Calculate/bench and show result PTYPE res = byteCode.run(); if ( rounds ) for (size_t i=0; i<rounds; i++) res = byteCode.run(); std::cout << std::setprecision(25) << ">>> Result = " << res << std::endl; // Need to be picky? Then check NaN/inf-errors with built-in flag. This is // fast and "catches" all common errors like x/0, sqrt(-3), asin(123) ... if ( byteCode.fltErr ) std::cerr << "Float error fetched by ATMSP default-check" << std::endl; // Extra paranoia about NaN/inf float/complex errors? Due to the nature of // floats, the built-in check may not catch every case. Here how to fetch // these rare errors of more theoretical nature. But as they may happen: #if !defined(MPFR) if ( hasNumErr(res) ) std::cerr << "Float error fetched by extra-check" << std::endl; #endif }
//------------------------------------------------------------------------------------------------- double BenchATMSP::DoBenchmark(const std::string& sExpr, long iCount) { ATMSB<double> bc; // Parsing/bytecode generation with error check. In a scope here JUST to // demonstrate that a parser-instance itself is NOT needed later on ATMSP<double> p; unsigned int err = p.parse(bc, sExpr, "a, b, c, x, y, z, w"); if (err) { StopTimerAndReport(p.errMessage(err)); } else { // Set variable values for x,y,z. Here always 1,2,3 bc.var[0] = 1.1; bc.var[1] = 2.2; bc.var[2] = 3.3; bc.var[3] = 2.123456; bc.var[4] = 3.123456; bc.var[5] = 4.123456; bc.var[6] = 5.123456; //Prime the I and D caches for the expression { double d0 = 0.0; double d1 = 0.0; for (std::size_t i = 0; i < priming_rounds; ++i) { if (i & 1) d0 += bc.run(); else d1 += bc.run(); } if ( (d0 == std::numeric_limits<double>::infinity()) && (d1 == std::numeric_limits<double>::infinity()) ) { printf("\n"); } } // Perform benchmark then return results double fRes (0); double fSum (0); fRes = bc.run(); StartTimer(); for (int j = 0; j < iCount; ++j) { fSum += bc.run(); std::swap(bc.var[0], bc.var[1]); std::swap(bc.var[3], bc.var[4]); } StopTimer(fRes, fSum, iCount); } return m_fTime1; }