From e9b78b546baf50149d121c96df823d44a709a97c Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Thu, 8 Jun 2017 11:52:27 +0200 Subject: Performance improvements --- src/uscxml/interpreter/BasicDelayedEventQueue.cpp | 4 +- src/uscxml/interpreter/FastMicroStep.cpp | 231 ++++-- src/uscxml/interpreter/FastMicroStep.h | 8 + src/uscxml/interpreter/InterpreterImpl.cpp | 10 +- src/uscxml/interpreter/InterpreterMonitor.h | 9 +- src/uscxml/messages/Event.cpp | 6 + src/uscxml/messages/Event.h | 16 +- src/uscxml/server/HTTPServer.cpp | 4 +- test/CMakeLists.txt | 1 + test/benchmarks/createBenchmarks.pl | 159 +++++ test/benchmarks/findLCCA.scxml | 828 ++++++++++++++++++++++ test/src/test-gen-c.cpp | 12 +- test/src/test-performance.cpp | 46 ++ 13 files changed, 1245 insertions(+), 89 deletions(-) create mode 100755 test/benchmarks/createBenchmarks.pl create mode 100644 test/benchmarks/findLCCA.scxml create mode 100644 test/src/test-performance.cpp diff --git a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp index ef194ca..885ff2c 100644 --- a/src/uscxml/interpreter/BasicDelayedEventQueue.cpp +++ b/src/uscxml/interpreter/BasicDelayedEventQueue.cpp @@ -194,7 +194,7 @@ Data BasicDelayedEventQueue::serialize() { Data serialized; for (auto event : _queue) { - struct callbackData cb = _callbackData[event.uuid]; + struct callbackData cb = _callbackData[event.getUUID()]; struct timeval delay = {0, 0}; struct timeval now = {0, 0}; @@ -222,7 +222,7 @@ void BasicDelayedEventQueue::deserialize(const Data& data) { std::lock_guard lock(_mutex); for (auto event : data["BasicDelayedEventQueue"].array) { Event e = Event::fromData(event["event"]); - enqueueDelayed(e, strTo(event["delay"]), e.uuid); + enqueueDelayed(e, strTo(event["delay"]), e.getUUID()); } } diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index 2a95c9c..7ca1171 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -589,6 +589,15 @@ TARGET_SET_ESTABLISHED: } } _cache.exitSet.clear(); + + // initialize bitarrays for step() + _exitSet = boost::dynamic_bitset(_states.size(), false); + _entrySet = boost::dynamic_bitset(_states.size(), false); + _targetSet = boost::dynamic_bitset(_states.size(), false); + _tmpStates = boost::dynamic_bitset(_states.size(), false); + _conflicts = boost::dynamic_bitset(_transitions.size(), false); + _transSet = boost::dynamic_bitset(_transitions.size(), false); + _isInitialized = true; } @@ -632,15 +641,16 @@ InterpreterState FastMicroStep::step(size_t blockMs) { return USCXML_INITIALIZED; } + std::set monitors = _callbacks->getMonitors(); size_t i, j, k; - boost::dynamic_bitset exitSet(_states.size(), false); - boost::dynamic_bitset entrySet(_states.size(), false); - boost::dynamic_bitset targetSet(_states.size(), false); - boost::dynamic_bitset tmpStates(_states.size(), false); + _exitSet.reset(); + _entrySet.reset(); + _targetSet.reset(); + _tmpStates.reset(); - boost::dynamic_bitset conflicts(_transitions.size(), false); - boost::dynamic_bitset transSet(_transitions.size(), false); + _conflicts.reset(); + _transSet.reset(); #ifdef USCXML_VERBOSE std::cerr << "Config: "; @@ -651,7 +661,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { return USCXML_FINISHED; if (_flags & USCXML_CTX_TOP_LEVEL_FINAL) { - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeCompletion); + USCXML_MONITOR_CALLBACK(monitors, beforeCompletion); /* exit all remaining states */ i = USCXML_NUMBER_STATES; @@ -682,7 +692,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { _flags |= USCXML_CTX_FINISHED; - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), afterCompletion); + USCXML_MONITOR_CALLBACK(monitors, afterCompletion); return USCXML_FINISHED; } @@ -690,9 +700,9 @@ InterpreterState FastMicroStep::step(size_t blockMs) { if (_flags == USCXML_CTX_PRISTINE) { - targetSet |= USCXML_GET_STATE(0).completion; + _targetSet |= USCXML_GET_STATE(0).completion; _flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep); + USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); goto ESTABLISH_ENTRYSET; } @@ -704,11 +714,44 @@ InterpreterState FastMicroStep::step(size_t blockMs) { if ((_event = _callbacks->dequeueInternal())) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeProcessingEvent, _event); + USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); goto SELECT_TRANSITIONS; } +#if 1 + /* manage uninvocations */ + i = _invocations.find_first(); + while(i != boost::dynamic_bitset::npos) { + /* uninvoke */ + if (!BIT_HAS(i, _configuration) && USCXML_GET_STATE(i).invoke.size() > 0) { + for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) { + _callbacks->uninvoke(*invIter); + } + BIT_CLEAR(i, _invocations); + } + i = _invocations.find_next(i); + } + /* manage invocations */ + i = _configuration.find_first(); + while(i != boost::dynamic_bitset::npos) { + /* invoke */ + if (!BIT_HAS(i, _invocations) && USCXML_GET_STATE(i).invoke.size() > 0) { + for (auto invIter = USCXML_GET_STATE(i).invoke.begin(); invIter != USCXML_GET_STATE(i).invoke.end(); invIter++) { + try { + _callbacks->invoke(*invIter); + } catch (ErrorEvent e) { + LOG(_callbacks->getLogger(), USCXML_WARN) << e; + // TODO: Shall we deliver the event into the interpreter runtime? + } catch (...) { + } + } + BIT_SET_AT(i, _invocations) + } + i = _configuration.find_next(i); + } +#else + for (i = 0; i < USCXML_NUMBER_STATES; i++) { /* uninvoke */ if (!BIT_HAS(i, _configuration) && BIT_HAS(i, _invocations)) { @@ -735,17 +778,18 @@ InterpreterState FastMicroStep::step(size_t blockMs) { BIT_SET_AT(i, _invocations) } } +#endif // we dequeued all internal events and ought to signal stable configuration if (!(_flags & USCXML_CTX_STABLE)) { - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), onStableConfiguration); + USCXML_MONITOR_CALLBACK(monitors, onStableConfiguration); _microstepConfigurations.clear(); _flags |= USCXML_CTX_STABLE; return USCXML_MACROSTEPPED; } if ((_event = _callbacks->dequeueExternal(blockMs))) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeProcessingEvent, _event); + USCXML_MONITOR_CALLBACK1(monitors, beforeProcessingEvent, _event); goto SELECT_TRANSITIONS; } @@ -773,7 +817,7 @@ SELECT_TRANSITIONS: /* is the transition active? */ if (BIT_HAS(USCXML_GET_TRANS(i).source, _configuration)) { /* is it non-conflicting? */ - if (!BIT_HAS(i, conflicts)) { + if (!BIT_HAS(i, _conflicts)) { /* is it spontaneous with an event or vice versa? */ if ((USCXML_GET_TRANS(i).event.size() == 0 && !_event) || (USCXML_GET_TRANS(i).event.size() != 0 && _event)) { @@ -785,15 +829,15 @@ SELECT_TRANSITIONS: _flags |= USCXML_CTX_TRANSITION_FOUND; /* transitions that are pre-empted */ - conflicts |= USCXML_GET_TRANS(i).conflicts; + _conflicts |= USCXML_GET_TRANS(i).conflicts; /* states that are directly targeted (resolve as entry-set later) */ - targetSet |= USCXML_GET_TRANS(i).target; + _targetSet |= USCXML_GET_TRANS(i).target; /* states that will be left */ - exitSet |= USCXML_GET_TRANS(i).exitSet; + _exitSet |= USCXML_GET_TRANS(i).exitSet; - BIT_SET_AT(i, transSet); + BIT_SET_AT(i, _transSet); } } } @@ -805,7 +849,7 @@ SELECT_TRANSITIONS: printStateNames(exitSet); #endif - exitSet &= _configuration; + _exitSet &= _configuration; if (_flags & USCXML_CTX_TRANSITION_FOUND) { // trigger more sppontaneuous transitions @@ -817,7 +861,7 @@ SELECT_TRANSITIONS: return USCXML_MICROSTEPPED; } - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep); + USCXML_MONITOR_CALLBACK(monitors, beforeMicroStep); #ifdef USCXML_VERBOSE std::cerr << "Targets: "; @@ -840,17 +884,17 @@ SELECT_TRANSITIONS: if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP) { /* a history state whose parent is about to be exited */ - if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, exitSet)) { - tmpStates = USCXML_GET_STATE(i).completion; + if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, _exitSet)) { + _tmpStates = USCXML_GET_STATE(i).completion; /* set those states who were enabled */ - tmpStates &= _configuration; + _tmpStates &= _configuration; /* clear current history with completion mask */ _history &= ~(USCXML_GET_STATE(i).completion); /* set history */ - _history |= tmpStates; + _history |= _tmpStates; } } @@ -858,27 +902,26 @@ SELECT_TRANSITIONS: ESTABLISH_ENTRYSET: /* calculate new entry set */ - entrySet = targetSet; + _entrySet = _targetSet; /* iterate for ancestors */ - i = entrySet.find_first(); + i = _entrySet.find_first(); while(i != boost::dynamic_bitset::npos) { - entrySet |= USCXML_GET_STATE(i).ancestors; - i = entrySet.find_next(i); + _entrySet |= USCXML_GET_STATE(i).ancestors; + i = _entrySet.find_next(i); } /* iterate for descendants */ - i = entrySet.find_first(); + i = _entrySet.find_first(); while(i != boost::dynamic_bitset::npos) { - switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { case USCXML_STATE_FINAL: case USCXML_STATE_ATOMIC: break; case USCXML_STATE_PARALLEL: { - entrySet |= USCXML_GET_STATE(i).completion; + _entrySet |= USCXML_GET_STATE(i).completion; break; } @@ -890,32 +933,32 @@ ESTABLISH_ENTRYSET: /* nothing set for history, look for a default transition */ for (j = 0; j < _transitions.size(); j++) { if unlikely(USCXML_GET_TRANS(j).source == i) { - entrySet |= USCXML_GET_TRANS(j).target; + _entrySet |= USCXML_GET_TRANS(j).target; if(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP && !BIT_HAS_AND(USCXML_GET_TRANS(j).target, USCXML_GET_STATE(i).children)) { for (k = i + 1; k < _states.size(); k++) { if (BIT_HAS(k, USCXML_GET_TRANS(j).target)) { - entrySet |= USCXML_GET_STATE(k).ancestors; + _entrySet |= USCXML_GET_STATE(k).ancestors; break; } } } - BIT_SET_AT(j, transSet); + BIT_SET_AT(j, _transSet); break; } /* Note: SCXML mandates every history to have a transition! */ } } else { - tmpStates = USCXML_GET_STATE(i).completion; - tmpStates &= _history; - entrySet |= tmpStates; + _tmpStates = USCXML_GET_STATE(i).completion; + _tmpStates &= _history; + _entrySet |= _tmpStates; if (USCXML_GET_STATE(i).type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { /* a deep history state with nested histories -> more completion */ for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { if (BIT_HAS(j, USCXML_GET_STATE(i).completion) && - BIT_HAS(j, entrySet) && + BIT_HAS(j, _entrySet) && (USCXML_GET_STATE(j).type & USCXML_STATE_HAS_HISTORY)) { for (k = j + 1; k < USCXML_NUMBER_STATES; k++) { /* add nested history to entry_set */ @@ -923,7 +966,7 @@ ESTABLISH_ENTRYSET: USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_HISTORY_SHALLOW) && BIT_HAS(k, USCXML_GET_STATE(j).children)) { /* a nested history state */ - BIT_SET_AT(k, entrySet); + BIT_SET_AT(k, _entrySet); } } } @@ -936,12 +979,12 @@ ESTABLISH_ENTRYSET: case USCXML_STATE_INITIAL: { for (j = 0; j < USCXML_NUMBER_TRANS; j++) { if (USCXML_GET_TRANS(j).source == i) { - BIT_SET_AT(j, transSet); - BIT_CLEAR(i, entrySet); - entrySet |= USCXML_GET_TRANS(j).target; + BIT_SET_AT(j, _transSet); + BIT_CLEAR(i, _entrySet); + _entrySet |= USCXML_GET_TRANS(j).target; for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { if (BIT_HAS(k, USCXML_GET_TRANS(j).target)) { - entrySet |= USCXML_GET_STATE(k).ancestors; + _entrySet |= USCXML_GET_STATE(k).ancestors; } } } @@ -949,15 +992,15 @@ ESTABLISH_ENTRYSET: break; } case USCXML_STATE_COMPOUND: { /* we need to check whether one child is already in entry_set */ - if (!BIT_HAS_AND(entrySet, USCXML_GET_STATE(i).children) && + if (!BIT_HAS_AND(_entrySet, USCXML_GET_STATE(i).children) && (!BIT_HAS_AND(_configuration, USCXML_GET_STATE(i).children) || - BIT_HAS_AND(exitSet, USCXML_GET_STATE(i).children))) { - entrySet |= USCXML_GET_STATE(i).completion; + BIT_HAS_AND(_exitSet, USCXML_GET_STATE(i).children))) { + _entrySet |= USCXML_GET_STATE(i).completion; if (!BIT_HAS_AND(USCXML_GET_STATE(i).completion, USCXML_GET_STATE(i).children)) { /* deep completion */ for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { if (BIT_HAS(j, USCXML_GET_STATE(i).completion)) { - entrySet |= USCXML_GET_STATE(j).ancestors; + _entrySet |= USCXML_GET_STATE(j).ancestors; break; /* completion of compound is single state */ } } @@ -966,7 +1009,7 @@ ESTABLISH_ENTRYSET: break; } } - i = entrySet.find_next(i); + i = _entrySet.find_next(i); } @@ -979,9 +1022,9 @@ ESTABLISH_ENTRYSET: /* we cannot use find_first due to ordering */ i = USCXML_NUMBER_STATES; while(i-- > 0) { - if (BIT_HAS(i, exitSet) && BIT_HAS(i, _configuration)) { + if (BIT_HAS(i, _exitSet) && BIT_HAS(i, _configuration)) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(monitors, beforeExitingState, USCXML_GET_STATE(i).element); /* call all on exit handlers */ for (auto exitIter = USCXML_GET_STATE(i).onExit.begin(); exitIter != USCXML_GET_STATE(i).onExit.end(); exitIter++) { @@ -993,16 +1036,16 @@ ESTABLISH_ENTRYSET: } BIT_CLEAR(i, _configuration); - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(monitors, afterExitingState, USCXML_GET_STATE(i).element); } } /* TAKE_TRANSITIONS: */ - i = transSet.find_first(); + i = _transSet.find_first(); while(i != boost::dynamic_bitset::npos) { if ((USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(i).element); + USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, USCXML_GET_TRANS(i).element); if (USCXML_GET_TRANS(i).onTrans != NULL) { @@ -1014,10 +1057,10 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterTakingTransition, USCXML_GET_TRANS(i).element); + USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, USCXML_GET_TRANS(i).element); } - i = transSet.find_next(i); + i = _transSet.find_next(i); } #ifdef USCXML_VERBOSE @@ -1027,12 +1070,12 @@ ESTABLISH_ENTRYSET: /* ENTER_STATES: */ - i = entrySet.find_first(); + i = _entrySet.find_first(); while(i != boost::dynamic_bitset::npos) { if (BIT_HAS(i, _configuration)) { // already active - i = entrySet.find_next(i); + i = _entrySet.find_next(i); continue; } @@ -1040,11 +1083,11 @@ ESTABLISH_ENTRYSET: if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP || USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_INITIAL) { - i = entrySet.find_next(i); + i = _entrySet.find_next(i); continue; } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(monitors, beforeEnteringState, USCXML_GET_STATE(i).element); BIT_SET_AT(i, _configuration); @@ -1065,15 +1108,16 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(monitors, afterEnteringState, USCXML_GET_STATE(i).element); /* take history and initial transitions */ +#if 0 for (j = 0; j < USCXML_NUMBER_TRANS; j++) { - if unlikely(BIT_HAS(j, transSet) && + if unlikely(BIT_HAS(j, _transSet) && (USCXML_GET_TRANS(j).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && USCXML_GET_STATE(USCXML_GET_TRANS(j).source).parent == i) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(j).element); + USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, USCXML_GET_TRANS(j).element); /* call executable content in transition */ if (USCXML_GET_TRANS(j).onTrans != NULL) { @@ -1084,10 +1128,33 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterTakingTransition, USCXML_GET_TRANS(j).element); + USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, USCXML_GET_TRANS(j).element); } } +#else + j = _transSet.find_first(); + while(j != boost::dynamic_bitset::npos) { + if unlikely((USCXML_GET_TRANS(j).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && + USCXML_GET_STATE(USCXML_GET_TRANS(j).source).parent == i) { + + USCXML_MONITOR_CALLBACK1(monitors, beforeTakingTransition, USCXML_GET_TRANS(j).element); + + /* call executable content in transition */ + if (USCXML_GET_TRANS(j).onTrans != NULL) { + try { + _callbacks->process(USCXML_GET_TRANS(j).onTrans); + } catch (...) { + // do nothing and continue with next block + } + } + + USCXML_MONITOR_CALLBACK1(monitors, afterTakingTransition, USCXML_GET_TRANS(j).element); + } + + j = _transSet.find_next(j); + } +#endif /* handle final states */ if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_FINAL) { @@ -1106,30 +1173,56 @@ ESTABLISH_ENTRYSET: * 3. Iterate all active final states and remove their ancestors * 4. If a state remains, not all children of a parallel are final */ +#if 0 for (j = 0; j < USCXML_NUMBER_STATES; j++) { if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(j).type) == USCXML_STATE_PARALLEL && BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { - tmpStates.reset(); + _tmpStates.reset(); k = _configuration.find_first(); while (k != boost::dynamic_bitset::npos) { if (BIT_HAS(j, USCXML_GET_STATE(k).ancestors)) { if (USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_FINAL) { - tmpStates ^= USCXML_GET_STATE(k).ancestors; + _tmpStates ^= USCXML_GET_STATE(k).ancestors; } else { - BIT_SET_AT(k, tmpStates); + BIT_SET_AT(k, _tmpStates); } } k = _configuration.find_next(k); } - if (!tmpStates.any()) { + if (!_tmpStates.any()) { // raise done for state j _callbacks->raiseDoneEvent(USCXML_GET_STATE(j).element, USCXML_GET_STATE(j).doneData); } } } +#else + j = USCXML_GET_STATE(i).ancestors.find_first(); + while(j != boost::dynamic_bitset::npos) { + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(j).type) == USCXML_STATE_PARALLEL) { + _tmpStates.reset(); + k = _configuration.find_first(); + while (k != boost::dynamic_bitset::npos) { + if (BIT_HAS(j, USCXML_GET_STATE(k).ancestors)) { + if (USCXML_STATE_MASK(USCXML_GET_STATE(k).type) == USCXML_STATE_FINAL) { + _tmpStates ^= USCXML_GET_STATE(k).ancestors; + } else { + BIT_SET_AT(k, _tmpStates); + } + } + k = _configuration.find_next(k); + } + if (!_tmpStates.any()) { + // raise done for state j + _callbacks->raiseDoneEvent(USCXML_GET_STATE(j).element, USCXML_GET_STATE(j).doneData); + } + } + + j = USCXML_GET_STATE(i).ancestors.find_next(j); + } +#endif } } - USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), afterMicroStep); + USCXML_MONITOR_CALLBACK(monitors, afterMicroStep); // are we running in circles? if (_microstepConfigurations.find(_configuration) != _microstepConfigurations.end()) { @@ -1137,7 +1230,7 @@ ESTABLISH_ENTRYSET: NULL, InterpreterIssue::USCXML_ISSUE_WARNING); - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), + USCXML_MONITOR_CALLBACK1(monitors, reportIssue, issue); } diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index 6c5518d..ca1f697 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -149,6 +149,14 @@ private: CachedPredicates _cache; + boost::dynamic_bitset _exitSet; + boost::dynamic_bitset _entrySet; + boost::dynamic_bitset _targetSet; + boost::dynamic_bitset _tmpStates; + + boost::dynamic_bitset _conflicts; + boost::dynamic_bitset _transSet; + #ifdef USCXML_VERBOSE void printStateNames(const boost::dynamic_bitset& bitset); #endif diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index bc8d2b9..18b27f8 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -507,15 +507,15 @@ Event InterpreterImpl::dequeueExternal(size_t blockMs) { void InterpreterImpl::enqueue(const std::string& type, const std::string& target, size_t delayMs, const Event& sendEvent) { std::lock_guard lock(_delayMutex); - assert(sendEvent.uuid.length() > 0); - assert(_delayedEventTargets.find(sendEvent.uuid) == _delayedEventTargets.end()); + assert(sendEvent.getUUID().length() > 0); + assert(_delayedEventTargets.find(sendEvent.getUUID()) == _delayedEventTargets.end()); - _delayedEventTargets[sendEvent.uuid] = std::tuple(sendEvent.sendid, type, target); + _delayedEventTargets[sendEvent.getUUID()] = std::tuple(sendEvent.sendid, type, target); if (delayMs == 0) { Event copy(sendEvent); - return eventReady(copy, sendEvent.uuid); + return eventReady(copy, sendEvent.getUUID()); } else { - return _delayQueue.enqueueDelayed(sendEvent, delayMs, sendEvent.uuid); + return _delayQueue.enqueueDelayed(sendEvent, delayMs, sendEvent.getUUID()); } } diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h index 27222b4..70cac0c 100644 --- a/src/uscxml/interpreter/InterpreterMonitor.h +++ b/src/uscxml/interpreter/InterpreterMonitor.h @@ -34,16 +34,19 @@ catch (...) { LOG(USCXML_ERROR) << "An exception occurred when calling " #callba if (_state == USCXML_DESTROYED) { throw std::bad_weak_ptr(); } #define USCXML_MONITOR_CALLBACK(callbacks, function) { \ +if (callbacks.size() > 0) {\ Interpreter inptr = _callbacks->getInterpreter(); \ -for (auto callback : callbacks) { callback->function(inptr); } } +for (auto callback : callbacks) { callback->function(inptr); } } } #define USCXML_MONITOR_CALLBACK1(callbacks, function, arg1) { \ +if (callbacks.size() > 0) {\ Interpreter inptr = _callbacks->getInterpreter(); \ -for (auto callback : callbacks) { callback->function(inptr, arg1); } } +for (auto callback : callbacks) { callback->function(inptr, arg1); } } } #define USCXML_MONITOR_CALLBACK2(callbacks, function, arg1, arg2) { \ +if (callbacks.size() > 0) {\ Interpreter inptr = _callbacks->getInterpreter(); \ -for (auto callback : callbacks) { callback->function(inptr, arg1, arg2); } } +for (auto callback : callbacks) { callback->function(inptr, arg1, arg2); } } } // forward declare namespace XERCESC_NS { diff --git a/src/uscxml/messages/Event.cpp b/src/uscxml/messages/Event.cpp index 5b933dc..a7863c5 100644 --- a/src/uscxml/messages/Event.cpp +++ b/src/uscxml/messages/Event.cpp @@ -21,6 +21,12 @@ namespace uscxml { +Event::Event() : eventType(INTERNAL), hideSendId(false) { +} + +Event::Event(const std::string& name, Type type) : name(name), eventType(type), hideSendId(false) { +} + Event Event::fromData(const Data& data) { Event e; if (data.hasKey("data")) diff --git a/src/uscxml/messages/Event.h b/src/uscxml/messages/Event.h index f40a672..c4d818b 100644 --- a/src/uscxml/messages/Event.h +++ b/src/uscxml/messages/Event.h @@ -107,8 +107,8 @@ public: PLATFORM = 3 }; - Event() : eventType(INTERNAL), hideSendId(false), uuid(UUID::getUUID()) {} - explicit Event(const std::string& name, Type type = INTERNAL) : name(name), eventType(type), hideSendId(false) {} + Event(); + explicit Event(const std::string& name, Type type = INTERNAL); static Event fromData(const Data& data); bool operator< (const Event& other) const { @@ -197,6 +197,14 @@ public: return false; } + const std::string& getUUID() const { + // this is expensive - lazy initialization + if (uuid.length() == 0) { + uuid = UUID::getUUID(); + } + return uuid; + } + std::string raw; std::string name; Type eventType; @@ -208,7 +216,9 @@ public: Data data; std::map namelist; std::multimap params; - std::string uuid; // the sendid is not necessarily unique! + +private: + mutable std::string uuid; // the sendid is not necessarily unique! friend USCXML_API std::ostream& operator<< (std::ostream& os, const Event& event); }; diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index aa43002..29dd9a6 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -736,7 +736,9 @@ void HTTPServer::start() { void HTTPServer::run(void* instance) { HTTPServer* INSTANCE = (HTTPServer*)instance; while(INSTANCE->_isRunning) { - event_base_dispatch(INSTANCE->_base); + // getting this to be non-polling is somewhat tricky and changes among versions + event_base_loop(INSTANCE->_base, EVLOOP_ONCE | EVLOOP_NO_EXIT_ON_EMPTY); +// event_base_dispatch(INSTANCE->_base); } LOGD(USCXML_INFO) << "HTTP Server stopped" << std::endl; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9e5b61f..89962f9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -61,6 +61,7 @@ if(WITH_DM_LUA AND NOT BUILD_AS_PLUGINS) endif() USCXML_TEST_COMPILE(NAME test-url LABEL general/test-url FILES src/test-url.cpp) USCXML_TEST_COMPILE(NAME test-utf8 LABEL general/test-utf8 FILES src/test-utf8.cpp) +USCXML_TEST_COMPILE(BUILD_ONLY NAME test-performance LABEL general/test-performance FILES src/test-performance.cpp) USCXML_TEST_COMPILE(NAME test-lifecycle LABEL general/test-lifecycle FILES src/test-lifecycle.cpp) USCXML_TEST_COMPILE(NAME test-validating LABEL general/test-validating FILES src/test-validating.cpp) USCXML_TEST_COMPILE(NAME test-snippets LABEL general/test-snippets FILES src/test-snippets.cpp) diff --git a/test/benchmarks/createBenchmarks.pl b/test/benchmarks/createBenchmarks.pl new file mode 100755 index 0000000..231c843 --- /dev/null +++ b/test/benchmarks/createBenchmarks.pl @@ -0,0 +1,159 @@ +#!/usr/bin/perl -w + +use strict; +use List::Util qw[min max sum]; + +use warnings; +no warnings 'recursion'; + +use Getopt::Long qw(GetOptions); +use Data::Dumper; + +my %options = (); + +GetOptions( + \%options, + "depth-max=i", + "child-max=i", + "events-max=i", + "states-max=i", + "trans-max=i", + "random-seed=i" +); + +my $seed = $options{'random-seed'} || int(rand(2**31)); + +# $maxStates = 8; +# $maxTrans = 8 + +srand($seed); + +my $machine; +my $stateId = 1; + + +sub createFindLCCABenchmark { + my $where = shift; + + my $nestingDepth = 20; + my $parallelStates = 20; + + $$where->{'name'} = 'findLCCA'; + $$where->{'type'} = 'scxml'; + $$where->{'intial'} = ""; + for (my $i = 1; $i <= $parallelStates; $i++) { + $$where->{'initial'} .= "id" . ($i*$nestingDepth) . " "; + } + + $$where->{'children'}[0]->{'type'} = 'parallel'; + $$where->{'children'}[0]->{'id'} = "p0"; + for (my $i = 0; $i < $parallelStates; $i++) { + createFindLCCANestedCompounds(\$$where->{'children'}[0]->{'children'}, $nestingDepth, $nestingDepth * $parallelStates + 1); + } + + $$where->{'children'}[1]->{'type'} = 'state'; + $$where->{'children'}[1]->{'id'} = "id".$stateId++; + $$where->{'children'}[1]->{'transitions'}[0]->{'target'} = $$where->{'initial'}; + +} + +sub createFindLCCANestedCompounds { + my $where = shift; + my $amount = shift; + my $target = shift; + + if ($amount > 0) { + my $state; + $state->{'id'} = "id".$stateId++; + $state->{'type'} = "state"; + createFindLCCANestedCompounds(\$state->{'children'}, $amount - 1, $target); + + if ($amount == 1) { + $state->{'transitions'}[0]->{'target'} = "id".$target; + } + + push @{$$where}, $state; + } + + +} + + +sub writeState { + my $state = shift; + my $fh = shift; + + print $fh '<'.$state->{'type'}; + print $fh ' id="'.$state->{'id'} . '"'; + print $fh ' type="deep"' if exists $state->{'deep'}; + print $fh '>'; + + foreach (@{$state->{'children'}}) { + writeState($_, $fh); + } + + foreach (@{$state->{'transitions'}}) { + writeTransition($_, $fh); + } + + print $fh '{'type'} . '>'; + +}; + +sub writeTransition { + my $trans = shift; + my $fh = shift; + + print $fh '{'target'}; + print $fh ' event="' . $trans->{'event'} . '"' if $trans->{'event'}; + print $fh ' cond="' . $trans->{'cond'} . '"' if $trans->{'cond'}; + + if ($trans->{'execContent'}) { + print $fh '>'; + foreach (@{$trans->{'execContent'}}) { + print $fh $_; + } + print $fh ''; + } else { + print $fh '/>'; + } + +}; + +sub writeMachine { + my $machine = shift; + my $file = shift; + + open(my $fh, ">", $file) or die "Can't open > $file: $!"; + + print $fh '{'datamodel'}; + print $fh ' seed="' . $seed . '"'; + print $fh ' name="' . $machine->{'name'} . '"' if $machine->{'name'}; + print $fh ' initial="' . $machine->{'initial'} . '"' if $machine->{'initial'}; + print $fh '>'; + + foreach (@{$machine->{'children'}}) { + writeState($_, $fh); + } + + print $fh ''; +} + +sub xmllint { + my $file = shift; + `mv $file $file.unformatted.xml`; + `xmllint --format $file.unformatted.xml > $file`; + `rm $file.unformatted.xml`; +} + +{ + $machine = {}; + $stateId = 1; + + createFindLCCABenchmark(\$machine); + # print Dumper($machine); + writeMachine($machine, "findLCCA.scxml"); + xmllint("findLCCA.scxml"); +} diff --git a/test/benchmarks/findLCCA.scxml b/test/benchmarks/findLCCA.scxml new file mode 100644 index 0000000..de2e8ce --- /dev/null +++ b/test/benchmarks/findLCCA.scxml @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/src/test-gen-c.cpp b/test/src/test-gen-c.cpp index b7cee32..0f83da3 100644 --- a/test/src/test-gen-c.cpp +++ b/test/src/test-gen-c.cpp @@ -570,11 +570,11 @@ public: e.invokeid = USER_DATA(ctx)->invokeId; } - USER_DATA(ctx)->sendUUIDs[e.uuid] = std::make_tuple(e.sendid, target, type); + USER_DATA(ctx)->sendUUIDs[e.getUUID()] = std::make_tuple(e.sendid, target, type); if (delayMs > 0) { - USER_DATA(ctx)->delayQueue.enqueueDelayed(e, delayMs, e.uuid); + USER_DATA(ctx)->delayQueue.enqueueDelayed(e, delayMs, e.getUUID()); } else { - USER_DATA(ctx)->eventReady(e, e.uuid); + USER_DATA(ctx)->eventReady(e, e.getUUID()); } return USCXML_ERR_OK; @@ -840,9 +840,9 @@ public: //std::make_tuple(e.sendid, target, type); - std::string sendid = std::get<0>(sendUUIDs[e.uuid]); - std::string target = std::get<1>(sendUUIDs[e.uuid]); - std::string type = std::get<2>(sendUUIDs[e.uuid]); + std::string sendid = std::get<0>(sendUUIDs[e.getUUID()]); + std::string target = std::get<1>(sendUUIDs[e.getUUID()]); + std::string type = std::get<2>(sendUUIDs[e.getUUID()]); if (target == "#_internal") { e.eventType = Event::INTERNAL; diff --git a/test/src/test-performance.cpp b/test/src/test-performance.cpp new file mode 100644 index 0000000..8df740d --- /dev/null +++ b/test/src/test-performance.cpp @@ -0,0 +1,46 @@ +#include "uscxml/config.h" +#include "uscxml/Interpreter.h" +#include + +#include + +using namespace uscxml; +using namespace std::chrono; + +int main(int argc, char** argv) { + if (argc < 2) { + std::cout << "Expected filename as first parameter" << std::endl; + exit(EXIT_FAILURE); + } + + Interpreter interpreter = Interpreter::fromURL(argv[1]); + + InterpreterState state; + system_clock::time_point start = system_clock::now(); + + while((state = interpreter.step()) != InterpreterState::USCXML_INITIALIZED) {} + system_clock::time_point now = system_clock::now(); + + std::cout << "init: " << duration_cast(now - start).count() << "ms" << std::endl; + + start = system_clock::now(); + system_clock::time_point endTime = start + seconds(10); + system_clock::time_point report = start + seconds(1); + + unsigned long iterations = 0; + + while(true) { + now = system_clock::now(); + if (now > endTime) + break; + + interpreter.step(); + + iterations++; + if (now > report) { + report = now + seconds(1); + std::cout << "steps / sec: " << iterations << std::endl; + iterations = 0; + } + } +} -- cgit v0.12