From 19d4e8ae2e472dd364ffeff1e096d3f75d5251c4 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Mon, 3 Jul 2017 17:04:26 +0200 Subject: BEnchmarks and performance improvements --- TODO.txt | 4 +- contrib/benchmarks/LCCA.pdf | Bin 0 -> 27216 bytes contrib/benchmarks/Transitions.pdf | Bin 0 -> 30761 bytes contrib/benchmarks/apache-commons/benchmark.sh | 7 - contrib/benchmarks/apache-commons/pom.xml | 24 - .../java/org/uscxml/benchmark/BenchmarkTest.java | 96 --- contrib/benchmarks/apache/pom.xml | 24 + .../java/org/uscxml/benchmark/BenchmarkTest.java | 101 +++ contrib/benchmarks/gnuplot-style.plt | 36 + contrib/benchmarks/lxsc/benchmark.sh | 8 - contrib/benchmarks/lxsc/statesPerSecond.lua | 32 + contrib/benchmarks/lxsc/test-performance.lua | 38 - contrib/benchmarks/plot.sh | 72 ++ contrib/benchmarks/qt/StatesPerSecond.cpp | 401 ++++++++++ contrib/benchmarks/qt/statemachine.pro | 11 + contrib/benchmarks/run.sh | 347 +++++++++ contrib/benchmarks/scion/statesPerSecond.js | 50 ++ contrib/benchmarks/scxmlcc/makefile | 35 + contrib/benchmarks/scxmlcc/statesPerSecond.cpp | 47 ++ contrib/benchmarks/uscxml/statesPerSecond.cpp | 69 ++ src/uscxml/Interpreter.cpp | 11 +- src/uscxml/Interpreter.h | 14 + src/uscxml/interpreter/BasicContentExecutor.cpp | 14 +- src/uscxml/interpreter/FastMicroStep.cpp | 1 + src/uscxml/interpreter/FastMicroStep.h | 8 +- src/uscxml/interpreter/InterpreterImpl.cpp | 15 +- src/uscxml/interpreter/LargeMicroStep.cpp | 421 +++++++---- src/uscxml/interpreter/LargeMicroStep.h | 36 +- src/uscxml/interpreter/MicroStepImpl.h | 4 + src/uscxml/plugins/Factory.cpp | 44 +- src/uscxml/plugins/Factory.h | 40 +- src/uscxml/plugins/InvokerImpl.h | 2 +- test/benchmarks/LCCA.16.scxml | 1 + test/benchmarks/LCCA.256.scxml | 1 + test/benchmarks/LCCA.4.scxml | 1 + test/benchmarks/LCCA.512.scxml | 1 + test/benchmarks/LCCA.64.scxml | 1 + test/benchmarks/Transitions.16.scxml | 1 + test/benchmarks/Transitions.256.scxml | 1 + test/benchmarks/Transitions.4.scxml | 1 + test/benchmarks/Transitions.512.scxml | 1 + test/benchmarks/Transitions.64.scxml | 1 + test/benchmarks/createBenchmarks.pl | 164 ++-- test/benchmarks/finalParallel.scxml | 786 ------------------- test/benchmarks/findLCCA.scxml | 828 --------------------- test/src/test-performance.cpp | 25 + 46 files changed, 1809 insertions(+), 2016 deletions(-) create mode 100644 contrib/benchmarks/LCCA.pdf create mode 100644 contrib/benchmarks/Transitions.pdf delete mode 100755 contrib/benchmarks/apache-commons/benchmark.sh delete mode 100644 contrib/benchmarks/apache-commons/pom.xml delete mode 100644 contrib/benchmarks/apache-commons/src/test/java/org/uscxml/benchmark/BenchmarkTest.java create mode 100644 contrib/benchmarks/apache/pom.xml create mode 100644 contrib/benchmarks/apache/src/test/java/org/uscxml/benchmark/BenchmarkTest.java create mode 100644 contrib/benchmarks/gnuplot-style.plt delete mode 100755 contrib/benchmarks/lxsc/benchmark.sh create mode 100644 contrib/benchmarks/lxsc/statesPerSecond.lua delete mode 100644 contrib/benchmarks/lxsc/test-performance.lua create mode 100755 contrib/benchmarks/plot.sh create mode 100644 contrib/benchmarks/qt/StatesPerSecond.cpp create mode 100644 contrib/benchmarks/qt/statemachine.pro create mode 100755 contrib/benchmarks/run.sh create mode 100644 contrib/benchmarks/scion/statesPerSecond.js create mode 100644 contrib/benchmarks/scxmlcc/makefile create mode 100644 contrib/benchmarks/scxmlcc/statesPerSecond.cpp create mode 100644 contrib/benchmarks/uscxml/statesPerSecond.cpp create mode 100644 test/benchmarks/LCCA.16.scxml create mode 100644 test/benchmarks/LCCA.256.scxml create mode 100644 test/benchmarks/LCCA.4.scxml create mode 100644 test/benchmarks/LCCA.512.scxml create mode 100644 test/benchmarks/LCCA.64.scxml create mode 100644 test/benchmarks/Transitions.16.scxml create mode 100644 test/benchmarks/Transitions.256.scxml create mode 100644 test/benchmarks/Transitions.4.scxml create mode 100644 test/benchmarks/Transitions.512.scxml create mode 100644 test/benchmarks/Transitions.64.scxml delete mode 100644 test/benchmarks/finalParallel.scxml delete mode 100644 test/benchmarks/findLCCA.scxml diff --git a/TODO.txt b/TODO.txt index 020fdfa..f5b6d1b 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,2 @@ -Plugins -HTTPS -respond element Arraybuffers +PGO \ No newline at end of file diff --git a/contrib/benchmarks/LCCA.pdf b/contrib/benchmarks/LCCA.pdf new file mode 100644 index 0000000..4d5c051 Binary files /dev/null and b/contrib/benchmarks/LCCA.pdf differ diff --git a/contrib/benchmarks/Transitions.pdf b/contrib/benchmarks/Transitions.pdf new file mode 100644 index 0000000..34b0ef7 Binary files /dev/null and b/contrib/benchmarks/Transitions.pdf differ diff --git a/contrib/benchmarks/apache-commons/benchmark.sh b/contrib/benchmarks/apache-commons/benchmark.sh deleted file mode 100755 index ec312a1..0000000 --- a/contrib/benchmarks/apache-commons/benchmark.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -set -e - -# mvn archetype:generate -DgroupId=org.uscxml.benchmark -DartifactId=benchmark -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false - -mvn test \ No newline at end of file diff --git a/contrib/benchmarks/apache-commons/pom.xml b/contrib/benchmarks/apache-commons/pom.xml deleted file mode 100644 index 58f6f14..0000000 --- a/contrib/benchmarks/apache-commons/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - 4.0.0 - org.uscxml.benchmark - benchmark - jar - 1.0-SNAPSHOT - benchmark - http://maven.apache.org - - - - org.apache.commons - commons-scxml2 - 2.0-SNAPSHOT - - - junit - junit - 3.8.1 - test - - - diff --git a/contrib/benchmarks/apache-commons/src/test/java/org/uscxml/benchmark/BenchmarkTest.java b/contrib/benchmarks/apache-commons/src/test/java/org/uscxml/benchmark/BenchmarkTest.java deleted file mode 100644 index 0888646..0000000 --- a/contrib/benchmarks/apache-commons/src/test/java/org/uscxml/benchmark/BenchmarkTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.uscxml.benchmark; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -import java.net.URL; -import java.util.List; - -import org.apache.commons.scxml2.*; - -import org.apache.commons.scxml2.env.SimpleDispatcher; -import org.apache.commons.scxml2.env.SimpleErrorReporter; -import org.apache.commons.scxml2.env.Tracer; -import org.apache.commons.scxml2.io.SCXMLReader; -import org.apache.commons.scxml2.io.SCXMLReader.Configuration; -import org.apache.commons.scxml2.model.CustomAction; -import org.apache.commons.scxml2.model.EnterableState; -import org.apache.commons.scxml2.model.Transition; -import org.apache.commons.scxml2.model.SCXML; -import org.apache.commons.scxml2.model.TransitionTarget; -import org.apache.commons.scxml2.model.EnterableState; -import org.apache.commons.scxml2.model.TransitionTarget; - -/** - * Unit test for simple App. - */ -public class BenchmarkTest extends TestCase { - - class PerformanceListener extends SimpleErrorReporter implements SCXMLListener { - public long iterations = 0; - public long mark = System.currentTimeMillis(); - - public void onEntry(final EnterableState state) { - if (state.getId().equals("p0")) { - iterations++; - long now = System.currentTimeMillis(); - if (now - mark > 1000) { - System.out.println(iterations); - mark = now; - iterations = 0; - } - } - } - public void onExit(final EnterableState state) {} - public void onTransition(final TransitionTarget from, final TransitionTarget to, final Transition transition, String event) {} - - } - - public SCXML parse(final URL url, final List customActions) throws Exception { - Configuration configuration = new Configuration(null, null, customActions); - SCXML scxml = SCXMLReader.read(url, configuration); - return scxml; - } - - public SCXMLExecutor getExecutor(final SCXML scxml, final Evaluator evaluator, final EventDispatcher eventDispatcher) throws Exception { - PerformanceListener trc = new PerformanceListener(); - SCXMLExecutor exec = new SCXMLExecutor(evaluator, eventDispatcher, null); - exec.setStateMachine(scxml); - exec.addListener(scxml, trc); - return exec; - } - - /** - * Create the test case - * - * @param testName name of the test case - */ - public BenchmarkTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( BenchmarkTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - try { - SCXML scxml = parse(new URL("file:../../../test/benchmarks/findLCCA.scxml"), null); - SCXMLExecutor exec = getExecutor(scxml, null, new SimpleDispatcher()); - exec.go(); - } catch (Exception e) { - System.err.println(e); - assertTrue(false); - } - } -} diff --git a/contrib/benchmarks/apache/pom.xml b/contrib/benchmarks/apache/pom.xml new file mode 100644 index 0000000..58f6f14 --- /dev/null +++ b/contrib/benchmarks/apache/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + org.uscxml.benchmark + benchmark + jar + 1.0-SNAPSHOT + benchmark + http://maven.apache.org + + + + org.apache.commons + commons-scxml2 + 2.0-SNAPSHOT + + + junit + junit + 3.8.1 + test + + + diff --git a/contrib/benchmarks/apache/src/test/java/org/uscxml/benchmark/BenchmarkTest.java b/contrib/benchmarks/apache/src/test/java/org/uscxml/benchmark/BenchmarkTest.java new file mode 100644 index 0000000..08a9a38 --- /dev/null +++ b/contrib/benchmarks/apache/src/test/java/org/uscxml/benchmark/BenchmarkTest.java @@ -0,0 +1,101 @@ +package org.uscxml.benchmark; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import java.net.URL; +import java.util.List; + +import org.apache.commons.scxml2.*; + +import org.apache.commons.scxml2.env.SimpleDispatcher; +import org.apache.commons.scxml2.env.SimpleErrorReporter; +import org.apache.commons.scxml2.env.Tracer; +import org.apache.commons.scxml2.io.SCXMLReader; +import org.apache.commons.scxml2.io.SCXMLReader.Configuration; +import org.apache.commons.scxml2.model.CustomAction; +import org.apache.commons.scxml2.model.EnterableState; +import org.apache.commons.scxml2.model.Transition; +import org.apache.commons.scxml2.model.SCXML; +import org.apache.commons.scxml2.model.TransitionTarget; +import org.apache.commons.scxml2.model.EnterableState; +import org.apache.commons.scxml2.model.TransitionTarget; + +/** + * Unit test for simple App. + */ +public class BenchmarkTest extends TestCase { + + public long initMs = 0; + + class PerformanceListener extends SimpleErrorReporter implements SCXMLListener { + public long iterations = 0; + public long mark = System.currentTimeMillis(); + + public void onEntry(final EnterableState state) { + if (state.getId().equals("mark")) { + iterations++; + long now = System.currentTimeMillis(); + if (now - mark > 1000) { + System.out.println(initMs + ", " + iterations); + mark = now; + iterations = 0; + } + } + } + public void onExit(final EnterableState state) {} + public void onTransition(final TransitionTarget from, final TransitionTarget to, final Transition transition, String event) {} + + } + + public SCXML parse(final URL url, final List customActions) throws Exception { + Configuration configuration = new Configuration(null, null, customActions); + SCXML scxml = SCXMLReader.read(url, configuration); + return scxml; + } + + public SCXMLExecutor getExecutor(final SCXML scxml, final Evaluator evaluator, final EventDispatcher eventDispatcher) throws Exception { + PerformanceListener trc = new PerformanceListener(); + SCXMLExecutor exec = new SCXMLExecutor(evaluator, eventDispatcher, null); + exec.setStateMachine(scxml); + exec.addListener(scxml, trc); + return exec; + } + + /** + * Create the test case + * + * @param testName name of the test case + */ + public BenchmarkTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( BenchmarkTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + try { + long started = System.currentTimeMillis(); + String fileName = System.getenv("USCXML_BENCHMARK"); + SCXML scxml = parse(new URL("file:" + fileName), null); + SCXMLExecutor exec = getExecutor(scxml, null, new SimpleDispatcher()); + initMs = System.currentTimeMillis() - started; + exec.go(); + } catch (Exception e) { + System.err.println(e); + assertTrue(false); + } + } +} diff --git a/contrib/benchmarks/gnuplot-style.plt b/contrib/benchmarks/gnuplot-style.plt new file mode 100644 index 0000000..05cc0b1 --- /dev/null +++ b/contrib/benchmarks/gnuplot-style.plt @@ -0,0 +1,36 @@ +set loadpath \ + '~/Documents/TK/Code/gnuplot-colorbrewer/diverging' \ + '~/Documents/TK/Code/gnuplot-colorbrewer/qualitative' \ + '~/Documents/TK/Code/gnuplot-colorbrewer/sequential' + +load 'Dark2.plt' + +set datafile missing "NA" + +# set style line 1 lc rgb '#1B9E77' lw 1.5 pt 5 ps 0.2 +# set style line 2 lc rgb '#1B9E77' lw 1.5 pt 5 ps 0.2 dt 4 +# set style line 3 lc rgb '#D95F02' lw 1.5 pt 5 ps 0.2 +# set style line 4 lc rgb '#D95F02' lw 1.5 pt 5 ps 0.2 dt 4 +# set style line 5 lc rgb '#7570B3' lw 1.5 pt 5 ps 0.2 +# set style line 6 lc rgb '#7570B3' lw 1.5 pt 5 ps 0.2 dt 4 +# set style line 7 lc rgb '#E7298A' lw 1.5 pt 5 ps 0.2 +# set style line 8 lc rgb '#E7298A' lw 1.5 pt 5 ps 0.2 dt 4 +# set style line 20 lc rgb '#666666' lw 1.5 pt 5 ps 0.2 dt 4 + +set terminal pdf font 'Verdana,10' +# set termoption dash +# set palette negative +set termoption enhanced + +# deemphasize border +set style line 11 lc rgb '#505050' lt 4 +set border 11 back ls 11 + +# small grid +set style line 12 lc rgb '#808080' lt 0 lw 1 +set grid ytics back ls 12 + +set xtics nomirror out +# set ytics nomirror + +set xtics border rotate by -45 diff --git a/contrib/benchmarks/lxsc/benchmark.sh b/contrib/benchmarks/lxsc/benchmark.sh deleted file mode 100755 index d0860ad..0000000 --- a/contrib/benchmarks/lxsc/benchmark.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -set -e - -git clone https://github.com/Phrogz/LXSC.git -cp ../../../test/benchmarks/findLCCA.scxml ./test.scxml - -lua ./test-performance.lua \ No newline at end of file diff --git a/contrib/benchmarks/lxsc/statesPerSecond.lua b/contrib/benchmarks/lxsc/statesPerSecond.lua new file mode 100644 index 0000000..8469ea5 --- /dev/null +++ b/contrib/benchmarks/lxsc/statesPerSecond.lua @@ -0,0 +1,32 @@ +#!/usr/bin/env lua + +package.path = 'LXSC/?.lua;' .. package.path + +require 'io' +require 'os' +local LXSC = require 'lxsc' +local file = ... +local init, mark, now +local iterations = 0 + +local started = os.clock() +local xml = io.open(file):read("*all") +lxsc = LXSC:parse(xml) + +lxsc.onAfterEnter = function(id,kind) + if (id=="mark") then + iterations = iterations + 1 + now = os.clock() + if (now - mark > 1) then + print(init .. ", " .. iterations) + mark = now + iterations = 0 + end + end +end + +now = os.clock() +init = (now - started) * 1000; +mark = now + +lxsc:start() diff --git a/contrib/benchmarks/lxsc/test-performance.lua b/contrib/benchmarks/lxsc/test-performance.lua deleted file mode 100644 index 111fffa..0000000 --- a/contrib/benchmarks/lxsc/test-performance.lua +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env lua - -package.path = 'LXSC/?.lua;' .. package.path - -require 'io' -require 'os' -local LXSC = require 'lxsc' - -local c,t,lxsc = os.clock - -local out = io.open(string.format("results-%s.txt",LXSC.VERSION),"w") -local sum=0 -function mark(msg,t2,n) - local delta = (t2-t)*1000/(n or 1) - sum = sum + delta - out:write(string.format("%25s: %5.2fms\n",msg,delta)) -end - -local xml = io.open("test.scxml"):read("*all") -t = c() -for i=1,20 do lxsc = LXSC:parse(xml) end -mark("Parse XML",c(),20) - -lxsc.onAfterEnter = function(id,kind) - if (id=="id401") then - print("Entered "..kind.." '"..tostring(id).."'") - end -end - -t = c() -lxsc:start() -mark("Start Machine",c()) - - -out:write("----------------------------------\n") -out:write(string.format("%25s: %5.2fms ± 20%%\n","Total time",sum)) - -out:close() \ No newline at end of file diff --git a/contrib/benchmarks/plot.sh b/contrib/benchmarks/plot.sh new file mode 100755 index 0000000..c91ef70 --- /dev/null +++ b/contrib/benchmarks/plot.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +for bench in Transitions LCCA; do + rm logs/${bench}.log + + for size in 4 16 64 256 512; do + + for impl in lxsc scion qt scxmlcc apache uscxml-fast uscxml-large; do + log="logs/${bench}.${size}-${impl}.log" + if [ ! -s ${log} ]; then + for i in {1..10}; do + echo 0, 0 >> $log + done + fi + + # prepend implementation name as header + echo "${impl}.init.${size}", "${impl}" > ${log}.header + cat ${log} >> ${log}.header + + # analyze values with R +R -q -e "\ +library(psych); \ +x <- read.csv('${log}.header', head=TRUE, sep=","); \ +y <- describe(x, trim=.1, skew=FALSE); \ +write.table(y, file = \"${log}.r.csv\", sep=', ');" + tail -n2 ${log}.r.csv >> logs/${bench}.log + rm ${log}.header + done + done + +awk 'NR == 0 || NR % 2 == 0' logs/${bench}.log > logs/${bench}.even +awk 'NR == 0 || NR % 2 == 1' logs/${bench}.log > logs/${bench}.odd +paste -d', ' logs/${bench}.even /dev/null logs/${bench}.odd > logs/${bench}.joined.log + +cat << END_GNUPLOT > /tmp/tmp.plot + set title "Iterations per second for ${bench} Benchmark" + + load 'gnuplot-style.plt' + set boxwidth 0.25 relative + set style fill solid 0.25 border + set key right top + set offsets 0.5,0.5,0,0 + + # set yrange [0:*] + # set y2range [0:5000] + # set y2tics nomirror + set ytics nomirror + set xtics ("4^2" 0, "16^2" 1, "64^2" 2, "256^2" 3, "512^2" 4) + + set logscale y + + set ylabel "Iterations / sec" + set xlabel "Complexity" + # set y2label "Initialization [ms]" + + # see also https://stackoverflow.com/a/25512858/990120 + plot 'logs/${bench}.joined.log' \ + using (\$0):4:5 every 7::6 with yerrorlines title "uscxml large" axis x1y1, \ + '' using (\$0):4:5 every 7::5 with yerrorlines title "uscxml fast" axis x1y1, \ + '' using (\$0):4:5 every 7::3 with yerrorlines title "scxmlcc" axis x1y1, \ + '' using (\$0):4:5 every 7::4 with yerrorlines title "apache" axis x1y1, \ + '' using (\$0):4:5 every 7::1 with yerrorlines title "scion" axis x1y1, \ + '' using (\$0):4:5 every 7::0 with yerrorlines title "lxsc" axis x1y1, \ + '' using (\$0):4:5 every 7::2 with yerrorlines title "qt" axis x1y1, \ + # '' using (\$0 + 0.1):4:5:xtic(1) every 2::1 with yerrorlines title "Iterations per Second" axis x1y1 #, \ + #'' using (\$0 + 0.1):(\$4):(sprintf("%d", \$4)) every 2::1 with labels notitle offset char 0,1 +END_GNUPLOT + + gnuplot /tmp/tmp.plot > "logs/${bench}.pdf" + + +done; diff --git a/contrib/benchmarks/qt/StatesPerSecond.cpp b/contrib/benchmarks/qt/StatesPerSecond.cpp new file mode 100644 index 0000000..d79a245 --- /dev/null +++ b/contrib/benchmarks/qt/StatesPerSecond.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +Q_DECLARE_METATYPE(QScxmlError); + +enum { SpyWaitTime = 8000 }; + +class StatesPerSecond: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void connections(); +#if 0 + void stateNames_data(); + void stateNames(); + void activeStateNames_data(); + void activeStateNames(); + void onExit(); + void eventOccurred(); + + void doneDotStateEvent(); + void running(); +#endif +}; + +void StatesPerSecond::connections() +{ + QElapsedTimer timer; + QElapsedTimer duration; + timer.start(); + QScopedPointer stateMachine( + QScxmlStateMachine::fromFile(QString(getenv("USCXML_BENCHMARK")))); + std::cout << getenv("USCXML_BENCHMARK") << std::endl; + QVERIFY(!stateMachine.isNull()); + size_t initMs = 0; + size_t iterations = 0; + QMetaObject::Connection final = stateMachine->connectToState("mark", + [&iterations, &timer, &duration, &initMs](bool enabled) { + if (!enabled) + return; + iterations++; + if (timer.elapsed() > 1000) { + std::cout << initMs << ", " << iterations << std::endl; + iterations = 0; + timer.restart(); + } + if (duration.elapsed() > 10000) + exit(0); + }); + QVERIFY(final); + + initMs = timer.elapsed(); + + timer.restart(); + duration.start(); + stateMachine->start(); + + QTRY_VERIFY(iterations > 0); + +#if 0 + Receiver receiver; + + bool a1Reached = false; + bool finalReached = false; + QMetaObject::Connection a = stateMachine->connectToState("a", &receiver, &Receiver::a); + QVERIFY(a); + QMetaObject::Connection b = stateMachine->connectToState("b", &receiver, SLOT(b(bool))); + QVERIFY(b); + QMetaObject::Connection a1 = stateMachine->connectToState("a1", &receiver, + [&a1Reached](bool enabled) { + a1Reached = a1Reached || enabled; + }); + QVERIFY(a1); + QMetaObject::Connection final = stateMachine->connectToState("final", + [&finalReached](bool enabled) { + finalReached = finalReached || enabled; + }); + QVERIFY(final); + + bool a1Entered = false; + bool a1Exited = false; + bool finalEntered = false; + bool finalExited = false; + typedef QScxmlStateMachine QXSM; + + QMetaObject::Connection aEntry = stateMachine->connectToState( + "a", QXSM::onEntry(&receiver, &Receiver::aEnter)); + QVERIFY(aEntry); + QMetaObject::Connection aExit = stateMachine->connectToState( + "a", QXSM::onExit(&receiver, &Receiver::aExit)); + QVERIFY(aExit); + QMetaObject::Connection a1Entry = stateMachine->connectToState("a1", &receiver, + QXSM::onEntry([&a1Entered]() { + a1Entered = true; + })); + QVERIFY(a1Entry); + QMetaObject::Connection a1Exit = stateMachine->connectToState("a1", &receiver, + QXSM::onExit([&a1Exited]() { + a1Exited = true; + })); + QVERIFY(a1Exit); + + QMetaObject::Connection finalEntry = stateMachine->connectToState( + "final", QXSM::onEntry([&finalEntered]() { + finalEntered = true; + })); + QVERIFY(finalEntry); + + QMetaObject::Connection finalExit = stateMachine->connectToState( + "final", QXSM::onExit([&finalExited]() { + finalExited = true; + })); + QVERIFY(finalExit); + + stateMachine->start(); + + QTRY_VERIFY(a1Reached); + QTRY_VERIFY(finalReached); + QTRY_VERIFY(receiver.aReached); + QTRY_VERIFY(receiver.bReached); + + QVERIFY(disconnect(a)); + QVERIFY(disconnect(b)); + QVERIFY(disconnect(a1)); + QVERIFY(disconnect(final)); + +#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304 + QVERIFY(receiver.aEntered); + QVERIFY(!receiver.aExited); + QVERIFY(a1Entered); + QVERIFY(!a1Exited); + QVERIFY(finalEntered); + QVERIFY(!finalExited); + + QVERIFY(disconnect(aEntry)); + QVERIFY(disconnect(aExit)); + QVERIFY(disconnect(a1Entry)); + QVERIFY(disconnect(a1Exit)); + QVERIFY(disconnect(finalEntry)); + QVERIFY(disconnect(finalExit)); +#endif +#endif +} + +#if 0 +void tst_StateMachine::stateNames_data() +{ + QTest::addColumn("scxmlFileName"); + QTest::addColumn("compressed"); + QTest::addColumn("expectedStates"); + + QTest::newRow("stateNames-compressed") << QString(":/tst_statemachine/statenames.scxml") + << true + << (QStringList() << QString("a1") << QString("a2") << QString("final")); + QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml") + << false + << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("a2") << QString("b") << QString("final")); + QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml") + << true + << (QStringList() << QString("a") << QString("b")); + QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml") + << false + << (QStringList() << QString("super_top") << QString("a") << QString("b")); + + QTest::newRow("ids1") << QString(":/tst_statemachine/ids1.scxml") + << false + << (QStringList() << QString("foo.bar") << QString("foo-bar") + << QString("foo_bar") << QString("_")); +} + +void tst_StateMachine::stateNames() +{ + QFETCH(QString, scxmlFileName); + QFETCH(bool, compressed); + QFETCH(QStringList, expectedStates); + + QScopedPointer stateMachine(QScxmlStateMachine::fromFile(scxmlFileName)); + QVERIFY(!stateMachine.isNull()); + QCOMPARE(stateMachine->parseErrors().count(), 0); + + QCOMPARE(stateMachine->stateNames(compressed), expectedStates); +} + +void tst_StateMachine::activeStateNames_data() +{ + QTest::addColumn("scxmlFileName"); + QTest::addColumn("compressed"); + QTest::addColumn("expectedStates"); + + QTest::newRow("stateNames-compressed") << QString(":/tst_statemachine/statenames.scxml") + << true + << (QStringList() << QString("a1") << QString("final")); + QTest::newRow("stateNames-notCompressed") << QString(":/tst_statemachine/statenames.scxml") + << false + << (QStringList() << QString("top") << QString("a") << QString("a1") << QString("b") << QString("final")); + QTest::newRow("stateNamesNested-compressed") << QString(":/tst_statemachine/statenamesnested.scxml") + << true + << (QStringList() << QString("a") << QString("b")); + QTest::newRow("stateNamesNested-notCompressed") << QString(":/tst_statemachine/statenamesnested.scxml") + << false + << (QStringList() << QString("super_top") << QString("a") << QString("b")); +} + +void tst_StateMachine::activeStateNames() +{ + QFETCH(QString, scxmlFileName); + QFETCH(bool, compressed); + QFETCH(QStringList, expectedStates); + + QScopedPointer stateMachine(QScxmlStateMachine::fromFile(scxmlFileName)); + QVERIFY(!stateMachine.isNull()); + + QSignalSpy stableStateSpy(stateMachine.data(), SIGNAL(reachedStableState())); + + stateMachine->start(); + + stableStateSpy.wait(5000); + + QCOMPARE(stateMachine->activeStateNames(compressed), expectedStates); +} + + +void tst_StateMachine::onExit() +{ +#if defined(__cpp_return_type_deduction) && __cpp_return_type_deduction == 201304 + // Test onExit being actually called + + typedef QScxmlStateMachine QXSM; + QScopedPointer stateMachine(QXSM::fromFile(QString(":/tst_statemachine/eventoccurred.scxml"))); + + Receiver receiver; + bool aExited1 = false; + + stateMachine->connectToState("a", QXSM::onExit([&aExited1]() { aExited1 = true; })); + stateMachine->connectToState("a", QXSM::onExit(&receiver, &Receiver::aExit)); + stateMachine->connectToState("a", QXSM::onExit(&receiver, "aEnter")); + { + // Should not crash + Receiver receiver2; + stateMachine->connectToState("a", QXSM::onEntry(&receiver2, &Receiver::aEnter)); + stateMachine->connectToState("a", QXSM::onEntry(&receiver2, "aExit")); + stateMachine->connectToState("a", QXSM::onExit(&receiver2, &Receiver::aExit)); + stateMachine->connectToState("a", QXSM::onExit(&receiver2, "aEnter")); + } + + stateMachine->start(); + QTRY_VERIFY(receiver.aEntered); + QTRY_VERIFY(receiver.aExited); + QTRY_VERIFY(aExited1); +#endif +} + +bool hasChildEventRouters(QScxmlStateMachine *stateMachine) +{ + // Cast to QObject, to avoid ambigous "children" member. + const QObject &parentRouter = QScxmlStateMachinePrivate::get(stateMachine)->m_router; + return !parentRouter.children().isEmpty(); +} + +void tst_StateMachine::eventOccurred() +{ + QScopedPointer stateMachine(QScxmlStateMachine::fromFile(QString(":/tst_statemachine/eventoccurred.scxml"))); + QVERIFY(!stateMachine.isNull()); + + qRegisterMetaType(); + QSignalSpy finishedSpy(stateMachine.data(), SIGNAL(finished())); + + int events = 0; + auto con1 = stateMachine->connectToEvent("internalEvent2", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 1); + QCOMPARE(event.name(), QString("internalEvent2")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con1); + + auto con2 = stateMachine->connectToEvent("externalEvent", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 2); + QCOMPARE(event.name(), QString("externalEvent")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con2); + + auto con3 = stateMachine->connectToEvent("timeout", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 3); + QCOMPARE(event.name(), QString("timeout")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con3); + + auto con4 = stateMachine->connectToEvent("done.*", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 4); + QCOMPARE(event.name(), QString("done.state.top")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con4); + + auto con5 = stateMachine->connectToEvent("done.state", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 5); + QCOMPARE(event.name(), QString("done.state.top")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con5); + + auto con6 = stateMachine->connectToEvent("done.state.top", [&events](const QScxmlEvent &event) { + QCOMPARE(++events, 6); + QCOMPARE(event.name(), QString("done.state.top")); + QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent); + }); + QVERIFY(con6); + + stateMachine->start(); + + finishedSpy.wait(5000); + QCOMPARE(events, 6); + + QVERIFY(disconnect(con1)); + QVERIFY(disconnect(con2)); + QVERIFY(disconnect(con3)); + QVERIFY(disconnect(con4)); + QVERIFY(disconnect(con5)); + QVERIFY(disconnect(con6)); + + QTRY_VERIFY(!hasChildEventRouters(stateMachine.data())); +} + +void tst_StateMachine::doneDotStateEvent() +{ + QScopedPointer stateMachine(QScxmlStateMachine::fromFile(QString(":/tst_statemachine/stateDotDoneEvent.scxml"))); + QVERIFY(!stateMachine.isNull()); + + QSignalSpy finishedSpy(stateMachine.data(), SIGNAL(finished())); + + stateMachine->start(); + finishedSpy.wait(5000); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(stateMachine->activeStateNames(true).size(), 1); + qDebug() << stateMachine->activeStateNames(true); + QVERIFY(stateMachine->activeStateNames(true).contains(QLatin1String("success"))); +} + +void tst_StateMachine::running() +{ + QScopedPointer stateMachine( + QScxmlStateMachine::fromFile(QString(":/tst_statemachine/statenames.scxml"))); + QVERIFY(!stateMachine.isNull()); + + QSignalSpy runningChangedSpy(stateMachine.data(), SIGNAL(runningChanged(bool))); + + QCOMPARE(stateMachine->isRunning(), false); + + stateMachine->start(); + + QCOMPARE(runningChangedSpy.count(), 1); + QCOMPARE(stateMachine->isRunning(), true); + + stateMachine->stop(); + + QCOMPARE(runningChangedSpy.count(), 2); + QCOMPARE(stateMachine->isRunning(), false); +} +#endif +QTEST_MAIN(StatesPerSecond) + +#include "StatesPerSecond.moc" + + diff --git a/contrib/benchmarks/qt/statemachine.pro b/contrib/benchmarks/qt/statemachine.pro new file mode 100644 index 0000000..d3eae77 --- /dev/null +++ b/contrib/benchmarks/qt/statemachine.pro @@ -0,0 +1,11 @@ +QT = core gui qml testlib scxml +CONFIG += testcase + +TARGET = StatesPerSecond +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += \ + StatesPerSecond.cpp diff --git a/contrib/benchmarks/run.sh b/contrib/benchmarks/run.sh new file mode 100755 index 0000000..ac13779 --- /dev/null +++ b/contrib/benchmarks/run.sh @@ -0,0 +1,347 @@ +#!/bin/bash +ME=`basename $0` +DIR="$( cd "$( dirname "$0" )" && pwd )" +CWD=`pwd` +cd ${DIR} + +TIMEOUT=25s + +RUN_QT=false +RUN_APACHE=false +RUN_SCION=false +RUN_SCXMLCC=false +RUN_USCXML=false +RUN_LXSC=false + +PLOT=false + +PATTERN='*.scxml' + +if [ ! -d "./logs" ]; then + mkdir logs +fi + +# https://superuser.com/a/473715 +function canonicalPath +{ + local path="$1" ; shift + if [ -d "$path" ] + then + echo "$(cd "$path" ; pwd)" + else + local b=$(basename "$path") + local p=$(dirname "$path") + echo "$(cd "$p" ; pwd)/$b" + fi +} + +# ===== qt ================ +function init-qt { + cd qt + if [ ! -x "./build/StatesPerSecond" ]; then + mkdir build + cd build + qmake .. + make + cd .. + fi + cd .. +} + +function run-qt { + BENCHMARK=$1 + SC_NAME=$2 + export USCXML_BENCHMARK=${BENCHMARK} + + cd qt/build + make + timeout ${TIMEOUT} ./StatesPerSecond |tee ../../logs/${SC_NAME}-qt.log + cd ../.. +} + +function clean-qt { + cd qt + rm -rf build + cd .. +} + +# ===== APACHE ================ +function init-apache { + cd apache + mvn compile + cd .. +} + +function run-apache { + BENCHMARK=$1 + SC_NAME=$2 + export USCXML_BENCHMARK=${BENCHMARK} + + cd apache + timeout ${TIMEOUT} mvn test |tee ../logs/${SC_NAME}-apache.log + cd .. + +} + +function clean-apache { + cd apache + rm -rf target + cd .. +} + + +# ===== USCXML ================ +function init-uscxml { + cd uscxml + if [ ! -x "./statesPerSecond" ]; then + g++ -std=c++11 \ + ./statesPerSecond.cpp \ + -I/usr/local/include \ + -I../../../build/cli/deps/xerces-c/include/ \ + -luscxml \ + -L ../../../build/cli/deps/xerces-c/lib/ \ + -lxerces-c \ + -o statesPerSecond + fi + cd .. +} + +function run-uscxml { + BENCHMARK=$1 + SC_NAME=$2 + + cd uscxml + timeout 600s ./statesPerSecond ${BENCHMARK} fast + timeout ${TIMEOUT} ./statesPerSecond ${BENCHMARK} fast |tee ../logs/${SC_NAME}-uscxml-fast.log + USCXML_NOCACHE_FILES=YES \ + timeout ${TIMEOUT} ./statesPerSecond ${BENCHMARK} large |tee ../logs/${SC_NAME}-uscxml-large.log + cd .. +} + +function clean-uscxml { + cd uscxml + rm statesPerSecond + cd .. + +} + +# ===== LXSC ================ +function init-lxsc { + cd lxsc + + if [ ! -d "./lxsc" ]; then + git clone https://github.com/Phrogz/LXSC.git + # increase microstep sequence considerably + sed -i '.bak' 's/S\.MAX_ITERATIONS\ =\ 1000/S\.MAX_ITERATIONS\ =\ 1000000/' ./LXSC/lib/runtime.lua + fi + + cd .. +} + +function run-lxsc { + BENCHMARK=$1 + SC_NAME=$2 + + cd lxsc + timeout ${TIMEOUT} lua ./statesPerSecond.lua ${BENCHMARK} |tee ../logs/${SC_NAME}-lxsc.log + cd .. +} + +function clean-lxsc { + cd lxsc + rm -rf LXSC + cd .. +} + +# ===== SCION ================ +function init-scion { + cd scion + if [ ! -d "./node_modules" ]; then + npm install scxml babel-polyfill + fi + cd .. +} + +function run-scion { + BENCHMARK=$1 + SC_NAME=$2 + + cd scion + + timeout ${TIMEOUT} node ./statesPerSecond.js ${BENCHMARK} |tee ../logs/${SC_NAME}-scion.log + + cd .. +} + +function clean-scion { + cd scion + rm -rf node_modules + cd .. +} + +# ===== SCXMLCC ================ +function init-scxmlcc { + cd scxmlcc + + # check out from git + if [ ! -d "./scxmlcc" ]; then + git clone https://github.com/jp-embedded/scxmlcc.git + fi + + # compile transpiler + if [ ! -x "./scxmlcc/src/scxmlcc" ]; then + export CXXFLAGS=-I/opt/local/include + cp ./makefile scxmlcc/src + cd scxmlcc/src + touch version_auto.h + make + cd ../.. + fi + + cd .. +} + +function run-scxmlcc { + BENCHMARK=$1 + SC_NAME=$2 + + cd scxmlcc + rm test + timeout 600s ./scxmlcc/src/scxmlcc -i ${BENCHMARK} -o ./test.h + timeout ${TIMEOUT} g++ -DMACHINE_NAME=sc_benchmark ./statesPerSecond.cpp -o test + timeout ${TIMEOUT} ./test |tee ../logs/${SC_NAME}-scxmlcc.log + + cd .. +} + +function clean-scxmlcc { + cd scxmlcc + rm -rf logs + rm -rf scxmlcc + rm test + rm test.h + cd .. +} + + +# ======================================= +while [[ $# -gt 0 ]] +do +key="$1" +case $key in + -p|--pattern) + PATTERN="$2" + ;; + + -p|--plot) + + ;; + -c|--clean) + clean-scxmlcc + clean-scion + clean-lxsc + clean-uscxml + clean-apache + clean-qt + rm -rf logs + exit + ;; + + --all) + init-scxmlcc + init-uscxml + init-lxsc + init-scion + init-apache + init-qt + RUN_QT=true + RUN_APACHE=true + RUN_SCXMLCC=true + RUN_USCXML=true + RUN_LXSC=true + RUN_SCION=true + ;; + + --scxmlcc) + init-scxmlcc + RUN_SCXMLCC=true + ;; + + --uscxml) + init-uscxml + RUN_USCXML=true + ;; + + --lxsc) + init-lxsc + RUN_LXSC=true + ;; + + --qt) + init-qt + RUN_QT=true + ;; + + --scion) + init-scion + RUN_SCION=true + ;; + + --apache) + init-apache + RUN_APACHE=true + ;; + + *) + ;; +esac +shift # past argument or value +done + +BENCHMARKS=`find $(pwd)/../../test/benchmarks -type f -name ${PATTERN}` + +for BENCHMARK in $BENCHMARKS +do + BENCHMARK=$(canonicalPath $BENCHMARK) + + SC_NAME=$(basename "$BENCHMARK" .scxml) + + echo "== Running ${SC_NAME} from ${BENCHMARK}" + + if [ "$RUN_SCION" = true ] ; then + echo "==== with SCION" + run-scion ${BENCHMARK} ${SC_NAME} + fi + + if [ "$RUN_SCXMLCC" = true ] ; then + echo "==== with SCXMLCC" + run-scxmlcc ${BENCHMARK} ${SC_NAME} + fi + + if [ "$RUN_LXSC" = true ] ; then + echo "==== with LXSC" + run-lxsc ${BENCHMARK} ${SC_NAME} + fi + + if [ "$RUN_QT" = true ] ; then + echo "==== with QT" + run-qt ${BENCHMARK} ${SC_NAME} + fi + + if [ "$RUN_USCXML" = true ] ; then + echo "==== with USCXML" + run-uscxml ${BENCHMARK} ${SC_NAME} + fi + + if [ "$RUN_APACHE" = true ] ; then + echo "==== with APACHE" + run-apache ${BENCHMARK} ${SC_NAME} + fi + +done +sed -i '.bak' '/^[0-9\.]*, [0-9\.]*/!d' ./logs/*.log +rm -f logs/*.bak + +if [ "$PLOT" = true ] ; then + ./plot.sh +fi diff --git a/contrib/benchmarks/scion/statesPerSecond.js b/contrib/benchmarks/scion/statesPerSecond.js new file mode 100644 index 0000000..2a15fcb --- /dev/null +++ b/contrib/benchmarks/scion/statesPerSecond.js @@ -0,0 +1,50 @@ +require('babel-polyfill'); +let scxml = require('scxml'); + +function nowInMS() { + var hrTime = process.hrtime(); + return hrTime[0] * 1000 + hrTime[1] / 1000000 +} + +var args = process.argv.splice(process.execArgv.length + 2); +var started = nowInMS(); +var initTimeMs; + +scxml.pathToModel(args[0], function(err,model){ + if(err) throw err; + + model.prepare(function(err, fnModel) { + + var iterations = 0; + var mark = nowInMS(); + + if(err) throw err; + + //instantiate the interpreter + var sc = new scxml.scion.Statechart(fnModel); + + initTimeMs = nowInMS() - started; + + sc.registerListener({onEntry : function(stateId) { + if (stateId == "mark") { + iterations++; + + var now = nowInMS(); + if (now - mark > 1000) { + console.log(initTimeMs + ", " + iterations); + mark = now; + iterations = 0; + } + + } + } + }); + + //start the interpreter + sc.start(); + + //send the init event + sc.gen({name:"init",data:null}); + + }); +}) \ No newline at end of file diff --git a/contrib/benchmarks/scxmlcc/makefile b/contrib/benchmarks/scxmlcc/makefile new file mode 100644 index 0000000..0466459 --- /dev/null +++ b/contrib/benchmarks/scxmlcc/makefile @@ -0,0 +1,35 @@ +#************************************************************************* +#** Copyright (C) 2013 Jan Pedersen +#** +#** This program is free software: you can redistribute it and/or modify +#** it under the terms of the GNU General Public License as published by +#** the Free Software Foundation, either version 3 of the License, or +#** (at your option) any later version. +#** +#** This program is distributed in the hope that it will be useful, +#** but WITHOUT ANY WARRANTY; without even the implied warranty of +#** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#** GNU General Public License for more details. +#** +#** You should have received a copy of the GNU General Public License +#** along with this program. If not, see . +#************************************************************************* + +OBJS := main.o cpp_output.o scxml_parser.o version.o +CPPFLAGS := -Wall -MD -MP -O2 + +all: scxmlcc + +scxmlcc: $(OBJS) + $(CXX) -o $@ $^ -L/opt/local/lib -lboost_program_options-mt -lboost_system-mt -lboost_filesystem-mt + +clean: + rm -f $(OBJS) $(OBJS:.o=.d) scxmlcc autorevision.mk version_auto.h + +autorevision.mk: + autorevision -tsh > $@ && sh makerevision.sh || truncate -s0 version_auto.h +.PHONY: autorevision.mk +-include autorevision.mk + +-include $(OBJS:.o=.d) + diff --git a/contrib/benchmarks/scxmlcc/statesPerSecond.cpp b/contrib/benchmarks/scxmlcc/statesPerSecond.cpp new file mode 100644 index 0000000..e0dc7b2 --- /dev/null +++ b/contrib/benchmarks/scxmlcc/statesPerSecond.cpp @@ -0,0 +1,47 @@ +#include +#include +#include + +#include "test.h" + +using namespace std; +using namespace std::chrono; + +typedef MACHINE_NAME sc; + +long iterations = 0; +long initMs = 0; +system_clock::time_point report; +system_clock::time_point endTime; + +template<> void sc::state_actions::enter(sc::data_model &m) +{ + iterations++; + system_clock::time_point now = system_clock::now(); + + if (now > report) { + report = now + seconds(1); + std::cout << initMs << ", " << iterations << std::endl; + iterations = 0; + } + if (now > endTime) { + ::exit(EXIT_SUCCESS); + } +} + +int main(int argc, char *argv[]) +{ + system_clock::time_point start = system_clock::now(); + + sc sc0; + system_clock::time_point now = system_clock::now(); + + initMs = duration_cast(now - start).count(); + + start = now; + report = start + seconds(1); + endTime = start + seconds(10); + + sc0.init(); + return 0; +} diff --git a/contrib/benchmarks/uscxml/statesPerSecond.cpp b/contrib/benchmarks/uscxml/statesPerSecond.cpp new file mode 100644 index 0000000..f6e62e6 --- /dev/null +++ b/contrib/benchmarks/uscxml/statesPerSecond.cpp @@ -0,0 +1,69 @@ +#include +#include +#include + +#include "uscxml/uscxml.h" +#include "uscxml/interpreter/InterpreterMonitor.h" +#include "uscxml/util/DOM.h" + +using namespace std; +using namespace uscxml; +using namespace std::chrono; + +long iterations = 0; +long initMs = 0; +system_clock::time_point now; +system_clock::time_point report; +system_clock::time_point endTime; + +class PerfMon : public InterpreterMonitor { + public: + virtual void beforeEnteringState(Interpreter& interpreter, const XERCESC_NS::DOMElement* state) { + if (HAS_ATTR(state, X("id")) && ATTR(state, X("id")) == "mark") { + iterations++; + now = system_clock::now(); + if (now > report) { + report = now + seconds(1); + std::cout << initMs << ", " << iterations << std::endl; + iterations = 0; + } + } + if (now > endTime) { + ::exit(EXIT_SUCCESS); + } + } +}; + +int main(int argc, char *argv[]) +{ + system_clock::time_point start = system_clock::now(); + + Interpreter sc = Interpreter::fromURL(argv[1]); + ActionLanguage al; + if (argc > 2) { + if (std::string(argv[2]) == "large") { + al.microStepper = Factory::getInstance()->createMicroStepper("large", (MicroStepCallbacks*)sc); + } else { + al.microStepper = Factory::getInstance()->createMicroStepper("fast", (MicroStepCallbacks*)sc); + } + } + sc.setActionLanguage(al); + + InterpreterState state = sc.step(); // initialize? + + PerfMon mon; + sc.addMonitor(&mon); + + now = system_clock::now(); + initMs = duration_cast(now - start).count(); + + start = now; + report = start + seconds(1); + endTime = start + seconds(10); + + while(true) { + sc.step(); + } + + return 0; +} diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 59a5084..b20013f 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -23,6 +23,7 @@ #include "uscxml/interpreter/InterpreterImpl.h" #include "uscxml/util/DOM.h" #include "uscxml/util/URL.h" +#include "uscxml/util/MD5.hpp" #include #include @@ -81,6 +82,7 @@ Interpreter Interpreter::fromXML(const std::string& xml, const std::string& base interpreterImpl->_document = parser->adoptDocument(); interpreterImpl->_baseURL = absUrl; + interpreterImpl->_md5 = md5(xml); InterpreterImpl::addInstance(interpreterImpl); } catch (const XERCESC_NS::SAXParseException& toCatch) { @@ -147,12 +149,10 @@ Interpreter Interpreter::fromDocument(XERCESC_NS::DOMDocument* dom, const std::s Interpreter Interpreter::fromURL(const std::string& url) { URL absUrl = normalizeURL(url); -#ifdef _WIN32 +#if 1 // Xercesc is hard to build with SSL on windows, whereas curl uses winssl - if (absUrl.scheme() == "https") { - return fromXML(absUrl.getInContent(), absUrl); - } -#endif + return fromXML(absUrl.getInContent(), absUrl); +#else std::shared_ptr interpreterImpl(new InterpreterImpl()); Interpreter interpreter(interpreterImpl); @@ -187,6 +187,7 @@ Interpreter Interpreter::fromURL(const std::string& url) { } return interpreter; +#endif } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 7cbcc2a..7b5ad06 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -45,6 +45,12 @@ class InterpreterMonitor; class InterpreterImpl; class InterpreterIssue; +class MicroStepCallbacks; +class DataModelCallbacks; +class IOProcessorCallbacks; +class ContentExecutorCallbacks; +class DelayedEventQueueCallbacks; +class InvokerCallbacks; /** * @ingroup interpreter @@ -223,6 +229,14 @@ public: return _impl; } + explicit operator MicroStepCallbacks*() { return (MicroStepCallbacks*)(_impl.get()); } + explicit operator DataModelCallbacks*() { return (DataModelCallbacks*)(_impl.get()); } + explicit operator IOProcessorCallbacks*() { return (IOProcessorCallbacks*)(_impl.get()); } + explicit operator ContentExecutorCallbacks*() { return (ContentExecutorCallbacks*)(_impl.get()); } + explicit operator DelayedEventQueueCallbacks*() { return (DelayedEventQueueCallbacks*)(_impl.get()); } + explicit operator InvokerCallbacks*() { return (InvokerCallbacks*)(_impl.get()); } + + protected: std::shared_ptr _impl; diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp index 8d278ec..56e3d10 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.cpp +++ b/src/uscxml/interpreter/BasicContentExecutor.cpp @@ -728,12 +728,14 @@ Data BasicContentExecutor::elementAsData(XERCESC_NS::DOMElement* element, bool a if (asExpression) // not actually used, but likely expected return Data(contentSS.str(), Data::INTERPRETED); - // test153, we need to throw for test150 in promela - Data d = _callbacks->getAsData(contentSS.str()); - if (!d.empty()) - return d; - - // never actually occurs with the w3c tests + // test153, we need to throw for test150 in promela, but we need to space normalize for test558 + try { + Data d = _callbacks->getAsData(contentSS.str()); + if (!d.empty()) + return d; + } catch ( ... ) {} + + // test558 return Data(spaceNormalize(contentSS.str()), Data::VERBATIM); } } diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index 4fdfc99..8b91191 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -20,6 +20,7 @@ #undef USCXML_VERBOSE //#undef WITH_CACHE_FILES +#include "uscxml/config.h" #include "FastMicroStep.h" #include "uscxml/util/DOM.h" #include "uscxml/util/String.h" diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index ca1f697..1b743ef 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -22,7 +22,6 @@ //#define USCXML_VERBOSE 1 -#include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/util/DOM.h" // X @@ -46,6 +45,8 @@ namespace uscxml { /** * @ingroup microstep * @ingroup impl + * + * MicroStep implementation backed by indexed bit-arrays. */ class FastMicroStep : public MicroStepImpl { public: @@ -53,6 +54,8 @@ public: virtual ~FastMicroStep(); virtual std::shared_ptr create(MicroStepCallbacks* callbacks); + std::string getName() { return "fast"; } + virtual InterpreterState step(size_t blockMs); virtual void reset(); virtual bool isInState(const std::string& stateId); @@ -63,6 +66,8 @@ public: virtual Data serialize(); protected: + FastMicroStep() {}; // only for factory + class Transition { public: Transition() : element(NULL), source(0), onTrans(NULL), type(0) {} @@ -161,6 +166,7 @@ private: void printStateNames(const boost::dynamic_bitset& bitset); #endif + friend class Factory; }; } diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 4d98609..00fbf41 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -38,6 +38,7 @@ #include #include #include +#include // remove #include "uscxml/interpreter/FastMicroStep.h" #include "uscxml/interpreter/LargeMicroStep.h" @@ -337,11 +338,15 @@ void InterpreterImpl::init() { // try to open chached data from resource directory std::string sharedTemp = URL::getTempDir(true); std::ifstream dataFS(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache"); - if (dataFS.is_open()) { - std::string cacheStr((std::istreambuf_iterator(dataFS)), - std::istreambuf_iterator()); - _cache = Data::fromJSON(cacheStr); - } + try { + if (dataFS.is_open()) { + std::string cacheStr((std::istreambuf_iterator(dataFS)), + std::istreambuf_iterator()); + _cache = Data::fromJSON(cacheStr); + } + } catch (...) { + remove(std::string(sharedTemp + PATH_SEPERATOR + md5(_baseURL) + ".uscxml.cache").c_str()); + } // get md5 of current document std::stringstream ss; diff --git a/src/uscxml/interpreter/LargeMicroStep.cpp b/src/uscxml/interpreter/LargeMicroStep.cpp index 50aa29c..83900c8 100644 --- a/src/uscxml/interpreter/LargeMicroStep.cpp +++ b/src/uscxml/interpreter/LargeMicroStep.cpp @@ -19,8 +19,10 @@ #include "LargeMicroStep.h" #include "uscxml/debug/Benchmark.h" +#include "uscxml/util/Predicates.h" #include +#include #define USCXML_CTX_PRISTINE 0x00 #define USCXML_CTX_SPONTANEOUS 0x01 @@ -113,6 +115,7 @@ void LargeMicroStep::reset() { _isCancelled = false; _flags = USCXML_CTX_PRISTINE; _configuration.clear(); + _configurationPostFix.clear(); _history.clear(); _initializedData.clear(); _invocations.clear(); @@ -214,12 +217,12 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { _xmlPrefix = std::string(_xmlPrefix) + ":"; } - resortStates(_scxml, _xmlPrefix); - - std::map stateIds; - std::map stateElements; - std::map transElements; + { + BENCHMARK("init resort states") + resortStates(_scxml, _xmlPrefix); + } + std::set stateIds; /** -- All things states -- */ @@ -240,7 +243,7 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { for (i = 0; i < _states.size(); i++) { _states[i] = new State(i); _states[i]->element = tmp.front(); - stateElements[_states[i]->element] = _states[i]; + _states[i]->element->setUserData(X("uscxmlState"), _states[i], NULL); tmp.pop_front(); } assert(tmp.size() == 0); @@ -262,11 +265,16 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { for (i = 0; i < _states.size(); i++) { // collect states with an id attribute if (HAS_ATTR(_states[i]->element, kXMLCharId)) { - stateIds[ATTR(_states[i]->element, kXMLCharId)] = i; + stateIds.insert(ATTR(_states[i]->element, kXMLCharId)); } + // TODO: Reserve space for ancestors? => Measure performance! + // check for executable content and datamodels if (_states[i]->element->getChildElementCount() > 0) { + // not every child element will be a child state, but we can shrink later + _states[i]->children.reserve(_states[i]->element->getChildElementCount()); + std::list entryList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onentry", _states[i]->element); std::list exitList = DOMUtils::filterChildElements(_xmlPrefix.str() + "onexit", _states[i]->element); std::list invokeList = DOMUtils::filterChildElements(_xmlPrefix.str() + "invoke", _states[i]->element); @@ -311,17 +319,14 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { _states[i]->type = USCXML_STATE_ATOMIC; } else if (isParallel(_states[i]->element)) { _states[i]->type = USCXML_STATE_PARALLEL; - } else if (isCompound(_states[i]->element)) { - _states[i]->type = USCXML_STATE_COMPOUND; - } else { // + } else { // and any other state _states[i]->type = USCXML_STATE_COMPOUND; } // establish the states' completion std::list completionList = getCompletion(_states[i]->element); - for (j = 0; completionList.size() > 0; j++) { - _states[i]->completion.insert(stateElements[completionList.front()]); + _states[i]->completion.insert((State*)completionList.front()->getUserData(X("uscxmlState"))); completionList.pop_front(); } assert(completionList.size() == 0); @@ -331,20 +336,16 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { _states[i]->type |= USCXML_STATE_HAS_HISTORY; } - // set the states parent + // set the states parent and add us as a children DOMNode* parent = _states[i]->element->getParentNode(); if (parent && parent->getNodeType() == DOMNode::ELEMENT_NODE) { - _states[i]->parent = stateElements[(DOMElement*)parent]; - } - - // set the states children - std::list childList; - for (auto childElem = _states[i]->element->getFirstElementChild(); childElem; childElem = childElem->getNextElementSibling()) { - if (isState(childElem, false)) { - childList.push_back(stateElements[childElem]); + _states[i]->parent = (State*)parent->getUserData(X("uscxmlState")); + if (_states[i]->parent != NULL) { + _states[i]->parent->children.push_back(_states[i]); + _states[i]->ancestors.insert(_states[i]->parent); + _states[i]->ancestors.insert(_states[i]->parent->ancestors.begin(), _states[i]->parent->ancestors.end()); } } - _states[i]->children = { std::make_move_iterator(std::begin(childList)), std::make_move_iterator(std::end(childList))}; } /** -- All things transitions -- */ @@ -360,59 +361,30 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { tmp = DOMUtils::filterChildElements(XML_PREFIX(_scxml).str() + "transition", tmp); _transitions.resize(tmp.size()); + _conflicting.resize(tmp.size()); + _compatible.resize(tmp.size()); for (i = 0; i < _transitions.size(); i++) { _transitions[i] = new Transition(i); _transitions[i]->element = tmp.front(); - transElements[_transitions[i]->element] = _transitions[i]; + _transitions[i]->element->setUserData(X("uscxmlTrans"), _transitions[i], NULL); tmp.pop_front(); } assert(tmp.size() == 0); for (i = 0; i < _transitions.size(); i++) { - // establish the transitions' exit set - assert(_transitions[i]->element != NULL); - - { - std::list exitList = getExitSet(_transitions[i]->element, _scxml); - - auto elemIter = exitList.begin(); - for (size_t j = 0; j < exitList.size(); j++) { - _transitions[i]->exitSet.insert(stateElements[*elemIter++]); - } - } - - // establish the transitions' conflict set - std::list conflictList; - - for (j = 0; j < _transitions.size(); j++) { - DOMElement* t1 = _transitions[i]->element; - DOMElement* t2 = _transitions[j]->element; - if ((getSourceState(t1) == getSourceState(t2)) || - (DOMUtils::isDescendant(getSourceState(t1), getSourceState(t2))) || - (DOMUtils::isDescendant(getSourceState(t2), getSourceState(t1))) || - (DOMUtils::hasIntersection(getExitSet(t1, _scxml), getExitSet(t2, _scxml)))) { - } else { - // compatible - conflictList.push_back(transElements[t2]); - } - } - _transitions[i]->compatible = {std::make_move_iterator(std::begin(conflictList)), std::make_move_iterator(std::end(conflictList))}; - - // establish the transitions' target set - std::list targetList; - { std::list targets = tokenize(ATTR(_transitions[i]->element, kXMLCharTarget)); + _transitions[i]->target.reserve(targets.size()); + for (auto tIter = targets.begin(); tIter != targets.end(); tIter++) { if (stateIds.find(*tIter) != stateIds.end()) { - targetList.push_back(stateElements[getState(*tIter, _scxml)]); + _transitions[i]->target.push_back((State*)getState(*tIter, _scxml)->getUserData(X("uscxmlState"))); } } } - _transitions[i]->target = {std::make_move_iterator(std::begin(targetList)), std::make_move_iterator(std::end(targetList))}; // the transition's type if (!HAS_ATTR(_transitions[i]->element, kXMLCharTarget)) { @@ -451,14 +423,30 @@ void LargeMicroStep::init(XERCESC_NS::DOMElement* scxml) { /* Connect states and transitions */ for (auto state : _states) { std::list transList = DOMUtils::filterChildElements(_xmlPrefix.str() + "transition", state->element); - state->transitions.resize(transList.size()); - - for (auto i = 0; transList.size() > 0; i++) { - auto trans = transList.front(); - transList.pop_front(); - transElements[trans]->source = state; - state->transitions[i] = transElements[trans]; + if (transList.size() > 0) { + state->transitions.resize(transList.size()); + for (auto i = 0; transList.size() > 0; i++) { + auto trans = transList.front(); + transList.pop_front(); + Transition* uscxmlTrans = ((Transition*)trans->getUserData(X("uscxmlTrans"))); + uscxmlTrans->source = state; + // save some memory? => Measure performance! +// uscxmlTrans->compatible.shrink_to_fit(); +// uscxmlTrans->exitSet.shrink_to_fit(); + + state->transitions[i] = uscxmlTrans; + } + // we need the postfix order for iterating transitions of active states + state->postFixOrder = (*(state->transitions.begin()))->postFixOrder; + } else { + state->postFixOrder = std::numeric_limits::max(); } + + // save some memory? => Measure performance! +// state->ancestors.shrink_to_fit(); + state->children.shrink_to_fit(); +// state->completion.shrink_to_fit(); + assert(transList.size() == 0); } @@ -472,15 +460,15 @@ InterpreterState LargeMicroStep::step(size_t blockMs) { } std::set monitors = _callbacks->getMonitors(); - + _exitSet.clear(); _entrySet.clear(); _targetSet.clear(); _tmpStates.clear(); _compatible.clear(); + _conflicting.clear(); _transSet.clear(); - boost::container::flat_set erase; if (_flags & USCXML_CTX_FINISHED) return USCXML_FINISHED; @@ -591,68 +579,156 @@ InterpreterState LargeMicroStep::step(size_t blockMs) { return USCXML_IDLE; SELECT_TRANSITIONS: - + // we read an event - unset stable to signal onstable again later _flags &= ~USCXML_CTX_STABLE; { BENCHMARK("select transitions"); - for (auto transition : _transitions) { // we need to iterate over all for ordering - - /* never select history or initial transitions automatically */ - if unlikely(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) - continue; - - /* is it even active */ - if (_configuration.find(transition->source) == _configuration.end()) - continue; + + // iterate active states in postfix order and find transitions + for (auto stateIter = _configurationPostFix.begin(); stateIter != _configurationPostFix.end();) { + State* state = *stateIter++; +// std::cout << (HAS_ATTR(state->element, kXMLCharId) ? ATTR(state->element, kXMLCharId) : "?"); +// std::cout << ": " << state->documentOrder << " - " << state->postFixOrder << std::endl; - /* is it spontaneous with an event or vice versa? */ - if ((transition->event.size() == 0 && _event) || - (transition->event.size() != 0 && !_event)) - continue; + for (auto transIter = state->transitions.begin(); transIter != state->transitions.end();) { + Transition* transition = *transIter++; - /* is it non-conflicting? */ - if ((_flags & USCXML_CTX_TRANSITION_FOUND) && _compatible.find(transition) == _compatible.end()) - continue; - - /* is it matched? */ - if (_event && !_callbacks->isMatched(_event, transition->event)) - continue; - - /* is it enabled? */ - if (transition->cond.size() > 0 && !_callbacks->isTrue(transition->cond)) - continue; + /* never select history or initial transitions automatically */ + if unlikely(transition->type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) + continue; - /* transitions that are pre-empted */ - if (_flags & USCXML_CTX_TRANSITION_FOUND) { - for (auto compIter = _compatible.begin(); compIter != _compatible.end();) { - if (transition->compatible.find(*compIter) == transition->compatible.end()) { - compIter = _compatible.erase(compIter); - } else { - compIter++; + /* is it spontaneous with an event or vice versa? */ + if ((transition->event.size() == 0 && _event) || + (transition->event.size() != 0 && !_event)) + continue; + + /* check whether it is explicitly conflicting or compatible, calculate if neither */ + if (_flags & USCXML_CTX_TRANSITION_FOUND) { + BENCHMARK("select transitions conflict & compatible calc"); + + if (_conflicting[transition->postFixOrder]) { + // this transition is explicitly conflicting + continue; + } + if (!_compatible[transition->postFixOrder]) { + // it is not explicitly compatible, we know nothing! + BENCHMARK("select transitions conflict & compatible calc no entry"); + + bool conflicts = false; + for (auto enabledTrans : _transSet) { + if (enabledTrans->compatible.find(transition->postFixOrder) != enabledTrans->compatible.end() || + (enabledTrans->conflicting.find(transition->postFixOrder) != enabledTrans->conflicting.end())) { + continue; + } + + std::pair exit1 = getExitSet(transition); + std::pair exit2 = getExitSet(enabledTrans); + + if (exit1.first != 0 && exit2.first != 0 && // empty domain + ((exit1.first <= exit2.first && exit1.second >= exit2.first) || + (exit2.first <= exit1.first && exit2.second >= exit1.first))) { + // it is conflicting! +// assert(uscxml::conflicts(t1, t2, _scxml)); + transition->conflicting.insert(enabledTrans->postFixOrder); + enabledTrans->conflicting.insert(transition->postFixOrder); + conflicts = true; + break; + } else { +// assert(!uscxml::conflicts(t1, t2, _scxml)); + transition->compatible.insert(enabledTrans->postFixOrder); + enabledTrans->compatible.insert(transition->postFixOrder); + } + } + if (conflicts) + continue; } } - } else { - _compatible = transition->compatible; - } - - /* remember that we found a transition */ - _flags |= USCXML_CTX_TRANSITION_FOUND; + + /* is it matched? */ + if (_event && !_callbacks->isMatched(_event, transition->event)) + continue; + + /* is it enabled? */ + if (transition->cond.size() > 0 && !_callbacks->isTrue(transition->cond)) + continue; + + // This transition is fine and ought to be taken! - /* states that are directly targeted (resolve as entry-set later) */ - _targetSet.insert(transition->target.begin(), transition->target.end()); - - /* states that will be left */ - for (auto config : _configuration) { - if (transition->exitSet.find(config) != transition->exitSet.end()) { - _exitSet.insert(config); + /* update conflicting and compatible transitions */ + if (_flags & USCXML_CTX_TRANSITION_FOUND) { + BENCHMARK("select transitions conflict & compatible update"); + + /* remove all compatible transitions not listed in ours */ + size_t i = _compatible.find_first(); + while(i != boost::dynamic_bitset::npos) { + if (transition->compatible.find(i) == transition->compatible.end()) { + _compatible[i] = false; + } + i = _compatible.find_next(i); + } + + /* add all conflicting transitions listed in ours */ + for (auto conflict : transition->conflicting) { + _conflicting[conflict] = true; + } + + } else { + /* Very first transition added to optimally transition set */ + for (auto compatible : transition->compatible) { + _compatible[compatible] = true; + } + for (auto conflict : transition->conflicting) { + _conflicting[conflict] = true; + } + } + + /* remember that we found a transition */ + _flags |= USCXML_CTX_TRANSITION_FOUND; + + /* states that are directly targeted (complete as entry-set later) */ + _targetSet.insert(transition->target.begin(), transition->target.end()); + + /* lazily initialize exit set */ + if (transition->exitSet.first == 0 && transition->exitSet.second == 0) { +// DOMElement* tmp = getTransitionDomain(transition->element, _scxml); +// State* domain1 = tmp == NULL ? NULL : (State*)tmp->getUserData(X("uscxmlState")); +// uint32_t domain2 = getTransitionDomain(transition); +// assert(domain1 == NULL ? domain2 == std::numeric_limits::max() : domain1->documentOrder == domain2); + +// std::pair orig = getExitSetCached(transition->element, _scxml); + transition->exitSet = getExitSet(transition); +// assert(transition->exitSet == orig); } + + /* states that will be left */ + for (auto config : _configuration) { + if (config->documentOrder >= transition->exitSet.first && config->documentOrder <= transition->exitSet.second) { + _exitSet.insert(config); + } + } + _transSet.insert(transition); + + // break and exit loop if we are at the end + if (stateIter == _configurationPostFix.end()) + break; + + /* move to next possible state that can have optimally enabled transitions */ + auto nextIter = stateIter; + nextIter++; // advance by one + while(nextIter != _configurationPostFix.end() && *nextIter == (*stateIter)->parent) { + // advance until we found a non-ancestor + nextIter++; + stateIter++; + } + + break; // next state } - - _transSet.insert(transition); } +// std::cout << "." << iters << std::flush; } + if (_flags & USCXML_CTX_TRANSITION_FOUND) { // trigger more sppontaneuous transitions _flags |= USCXML_CTX_SPONTANEOUS; @@ -761,11 +837,15 @@ ESTABLISH_ENTRYSET: // add all the target's ancestors for (auto target : transition->target) { +#if 1 + _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); +#else State* anc = target->parent; while(anc != NULL && _entrySet.find(anc) == _entrySet.end()) { _entrySet.insert(anc); anc = anc->parent; } +#endif } } _transSet.insert(transition); @@ -812,11 +892,15 @@ ESTABLISH_ENTRYSET: for (auto target : transition->target) { _entrySet.insert(target); // add all states between target and this state +#if 1 + _entrySet.insert(target->ancestors.begin(), target->ancestors.end()); +#else State* anc = target->parent; while(anc != NULL && anc != state) { _entrySet.insert(anc); anc = anc->parent; } +#endif } } break; @@ -838,23 +922,27 @@ ESTABLISH_ENTRYSET: } } - assert(state->completion.size() == 1); - State* completion = *(state->completion.begin()); - - _entrySet.insert(completion); + // completion of a compound maybe multiple states via initial attribute + _entrySet.insert(state->completion.begin(), state->completion.end()); /* deep completion */ { BENCHMARK("add descendants compound deep completion"); + for (auto completion : state->completion) { - if (std::binary_search(state->children.begin(), state->children.end(), completion)) - goto NEXT_DESCENDANT; + if (std::binary_search(state->children.begin(), state->children.end(), completion)) + continue; - // add deep completions ancestors - State* anc = completion->parent; - while(anc != NULL && anc != state) { - _entrySet.insert(anc); - anc = anc->parent; + // add deep completions ancestors +#if 1 + _entrySet.insert(completion->ancestors.begin(), completion->ancestors.end()); +#else + State* anc = completion->parent; + while(anc != NULL && anc != state) { + _entrySet.insert(anc); + anc = anc->parent; + } +#endif } } break; @@ -888,6 +976,7 @@ ESTABLISH_ENTRYSET: } _configuration.erase(state); + _configurationPostFix.erase(state); USCXML_MONITOR_CALLBACK1(monitors, afterExitingState, state->element); } @@ -936,7 +1025,8 @@ ESTABLISH_ENTRYSET: USCXML_MONITOR_CALLBACK1(monitors, beforeEnteringState, state->element); _configuration.insert(state); - + _configurationPostFix.insert(state); + /* initialize data */ if (state->data.size() > 0) { if (_initializedData.find(state) == _initializedData.end()) { @@ -1009,10 +1099,12 @@ ESTABLISH_ENTRYSET: State* anc = state->parent; while(anc != NULL) { - if (USCXML_STATE_MASK(anc->type) == USCXML_STATE_PARALLEL && - isInFinal(anc)) { - _callbacks->raiseDoneEvent(anc->element, anc->doneData); - break; // ancestors cannot be final either + if (USCXML_STATE_MASK(anc->type) == USCXML_STATE_PARALLEL) { + if (isInFinal(anc)) { + _callbacks->raiseDoneEvent(anc->element, anc->doneData); + } else { + break; // ancestors cannot be final either + } } anc = anc->parent; } @@ -1140,4 +1232,75 @@ void LargeMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { } } +std::pair LargeMicroStep::getExitSet(const Transition* transition) { + if (_exitSetCache.find(transition->postFixOrder) == _exitSetCache.end()) { + std::pair statesToExit; + uint32_t domain = getTransitionDomain(transition); + if (domain == std::numeric_limits::max()) + return statesToExit; + + State* domainState = _states[domain]; + + // start of exit set + statesToExit.first = domainState->documentOrder + 1; // do not include domain itself + + // end of exit set + XERCESC_NS::DOMElement* sibling = domainState->element->getNextElementSibling(); + while(sibling && !isState(sibling)) + sibling = sibling->getNextElementSibling(); + if (sibling) { + State* siblingState = (State*)sibling->getUserData(X("uscxmlState")); + statesToExit.second = siblingState->documentOrder - 1; + } else { + statesToExit.second = _states.size() - 1; + } + _exitSetCache[transition->postFixOrder] = statesToExit; + return statesToExit; + } + return _exitSetCache[transition->postFixOrder]; +} + +uint32_t LargeMicroStep::getTransitionDomain(const Transition* transition) { + if (transition->target.size() == 0) + return std::numeric_limits::max(); + + bool internal = (HAS_ATTR(transition->element, kXMLCharType) ? ATTR(transition->element, kXMLCharType) == "internal" : false); + if (internal && USCXML_STATE_MASK(transition->source->type) == USCXML_STATE_COMPOUND) { + for (auto target : transition->target) { + if (target->ancestors.find(transition->source) == target->ancestors.end()) { + goto BREAK_LOOP; + } + } + return transition->source->documentOrder; + } +BREAK_LOOP: + + // find LCCA + uint32_t ancestor = std::numeric_limits::max(); + + // reverse walk up! + for(auto ancIter = transition->source->ancestors.rbegin(); ancIter != transition->source->ancestors.rend(); ancIter++) { + State* anc = *ancIter; + + // LCCA has to be a compound + if (!(USCXML_STATE_MASK(anc->type) == USCXML_STATE_COMPOUND)) + continue; + + // that contains all states + for (auto target : transition->target) { + if (target->ancestors.find(anc) == target->ancestors.end()) { + goto NEXT_ANCESTOR; + } + } + ancestor = anc->documentOrder; + break; + NEXT_ANCESTOR:; + } + + // none found - take uppermost root as ancestor + if (ancestor == std::numeric_limits::max()) + return 0; + return ancestor; +} + } diff --git a/src/uscxml/interpreter/LargeMicroStep.h b/src/uscxml/interpreter/LargeMicroStep.h index 3443d80..0d7d467 100644 --- a/src/uscxml/interpreter/LargeMicroStep.h +++ b/src/uscxml/interpreter/LargeMicroStep.h @@ -22,7 +22,6 @@ #ifndef LARGEMICROSTEP_H_2573547 #define LARGEMICROSTEP_H_2573547 -#include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/util/DOM.h" // X @@ -30,19 +29,27 @@ #include "uscxml/util/String.h" #include "uscxml/interpreter/InterpreterMonitor.h" -/* flat_set is 10 times faster than std::set here */ #include +#include #include #include #include #include "MicroStepImpl.h" +#ifdef _WIN32 +#define BITSET_BLOCKTYPE size_t +#else +#define BITSET_BLOCKTYPE +#endif + namespace uscxml { /** * @ingroup microstep * @ingroup impl + * + * MicroStep implementation with a more economic growth of data-structures for large state-charts. */ class LargeMicroStep : public MicroStepImpl { public: @@ -51,6 +58,8 @@ public: virtual ~LargeMicroStep(); virtual std::shared_ptr create(MicroStepCallbacks* callbacks); + std::string getName() { return "large"; } + virtual InterpreterState step(size_t blockMs); virtual void reset(); virtual bool isInState(const std::string& stateId); @@ -61,6 +70,8 @@ public: virtual Data serialize() { return Data(); } protected: + LargeMicroStep() {} // only for the factory + class State; class Transition; @@ -68,6 +79,10 @@ protected: { bool operator()(const State* lhs, const State* rhs) const { return lhs->documentOrder < rhs->documentOrder; } }; + struct StateOrderPostFix + { + bool operator()(const State* lhs, const State* rhs) const { return lhs->postFixOrder < rhs->postFixOrder; } + }; struct TransitionOrder { @@ -78,6 +93,7 @@ protected: std::vector _transitions; ///< Transitions in reverse post-order boost::container::flat_set _configuration; + boost::container::flat_set _configurationPostFix; boost::container::flat_set _invocations; boost::container::flat_set _history; boost::container::flat_set _initializedData; @@ -88,8 +104,9 @@ protected: const uint32_t postFixOrder; // making these const increases performance somewhat XERCESC_NS::DOMElement* element = NULL; - boost::container::flat_set compatible; - boost::container::flat_set exitSet; + boost::container::flat_set compatible; + boost::container::flat_set conflicting; + std::pair exitSet; State* source = NULL; std::vector target; @@ -107,9 +124,11 @@ protected: public: State(uint32_t documentOrder) : documentOrder(documentOrder) {} const uint32_t documentOrder; + uint32_t postFixOrder; XERCESC_NS::DOMElement* element; boost::container::flat_set completion; + boost::container::flat_set ancestors; // TODO: leverage! std::vector children; State* parent = NULL; @@ -152,7 +171,9 @@ private: boost::container::flat_set _targetSet; boost::container::flat_set _tmpStates; - boost::container::flat_set _compatible; + boost::dynamic_bitset _compatible; + boost::dynamic_bitset _conflicting; + boost::container::flat_set _transSet; // adapted from http://www.cplusplus.com/reference/algorithm/set_intersection/ @@ -173,6 +194,11 @@ private: bool isInFinal(const State* state); void printStateNames(const boost::container::flat_set& a); + uint32_t getTransitionDomain(const Transition* transition); + std::pair getExitSet(const Transition* transition); + std::map > _exitSetCache; + + friend class Factory; }; } diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index 7ff9469..53139bc 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -91,7 +91,11 @@ public: virtual void deserialize(const Data& encodedState) = 0; virtual Data serialize() = 0; + /// To register at the factory + virtual std::string getName() = 0; + protected: + MicroStepImpl() {}; MicroStepCallbacks* _callbacks; }; diff --git a/src/uscxml/plugins/Factory.cpp b/src/uscxml/plugins/Factory.cpp index 4c67d1f..410ac36 100644 --- a/src/uscxml/plugins/Factory.cpp +++ b/src/uscxml/plugins/Factory.cpp @@ -33,6 +33,11 @@ #include "uscxml/plugins/InvokerImpl.h" #include "uscxml/plugins/DataModelImpl.h" +#ifndef FEATS_ON_CMD +#include "uscxml/interpreter/LargeMicroStep.h" +#include "uscxml/interpreter/FastMicroStep.h" +#endif + #if 0 #include #include @@ -127,6 +132,11 @@ Factory::~Factory() { void Factory::registerPlugins() { +#ifndef FEATS_ON_CMD + registerMicrostepper(new LargeMicroStep()); + registerMicrostepper(new FastMicroStep()); +#endif + /*** PLUGINS ***/ #ifdef BUILD_AS_PLUGINS @@ -435,7 +445,6 @@ std::shared_ptr Factory::createDataModel(const std::string& type, return std::shared_ptr(); } - bool Factory::hasIOProcessor(const std::string& type) { if (_ioProcessorAliases.find(type) != _ioProcessorAliases.end()) { return true; @@ -495,6 +504,39 @@ std::shared_ptr Factory::createExecutableContent(const st } +#ifndef FEATS_ON_CMD + +bool Factory::hasMicroStepper(const std::string& name) { + if (_microSteppers.find(name) != _microSteppers.end()) { + return true; + } else if(_parentFactory) { + return _parentFactory->hasMicroStepper(name); + } + return false; +} + + +void Factory::registerMicrostepper(MicroStepImpl* microStepper) { + _microSteppers[microStepper->getName()] = microStepper; +} + +std::shared_ptr Factory::createMicroStepper(const std::string& name, MicroStepCallbacks* callbacks) { + if (_microSteppers.find(name) != _microSteppers.end()) { + std::shared_ptr microStepper = _microSteppers[name]->create(callbacks); + return microStepper; + } + + if (_parentFactory) { + return _parentFactory->createMicroStepper(name, callbacks); + } else { + ERROR_EXECUTION_THROW("No Microstepper '" + name + "' known"); + } + + return std::shared_ptr(); + +} +#endif + void DataModelImpl::addExtension(DataModelExtension* ext) { ERROR_EXECUTION_THROW("DataModel does not support extensions"); } diff --git a/src/uscxml/plugins/Factory.h b/src/uscxml/plugins/Factory.h index 0026df1..986ff5f 100644 --- a/src/uscxml/plugins/Factory.h +++ b/src/uscxml/plugins/Factory.h @@ -44,6 +44,8 @@ class DataModelCallbacks; class InvokerImpl; class InvokerCallbacks; class ExecutableContentImpl; +class MicroStepImpl; +class MicroStepCallbacks; class USCXML_API Factory { public: @@ -51,21 +53,31 @@ public: Factory(const std::string& pluginPath, Factory* parentFactory); void registerIOProcessor(IOProcessorImpl* ioProcessor); - void registerDataModel(DataModelImpl* dataModel); - void registerInvoker(InvokerImpl* invoker); - void registerExecutableContent(ExecutableContentImpl* executableContent); + bool hasIOProcessor(const std::string& type); + std::shared_ptr createIOProcessor(const std::string& type, IOProcessorCallbacks* callbacks); - std::shared_ptr createDataModel(const std::string& type, DataModelCallbacks* callbacks); - std::shared_ptr createIOProcessor(const std::string& type, IOProcessorCallbacks* callbacks); - std::shared_ptr createInvoker(const std::string& type, InvokerCallbacks* interpreter); - std::shared_ptr createExecutableContent(const std::string& localName, const std::string& nameSpace, InterpreterImpl* interpreter); + void registerDataModel(DataModelImpl* dataModel); + bool hasDataModel(const std::string& type); + std::shared_ptr createDataModel(const std::string& type, DataModelCallbacks* callbacks); - bool hasDataModel(const std::string& type); - bool hasIOProcessor(const std::string& type); - bool hasInvoker(const std::string& type); - bool hasExecutableContent(const std::string& localName, const std::string& nameSpace); + void registerInvoker(InvokerImpl* invoker); + bool hasInvoker(const std::string& type); + std::shared_ptr createInvoker(const std::string& type, InvokerCallbacks* interpreter); - std::map getIOProcessors(); + void registerExecutableContent(ExecutableContentImpl* executableContent); + bool hasExecutableContent(const std::string& localName, const std::string& nameSpace); + std::shared_ptr createExecutableContent(const std::string& localName, const std::string& nameSpace, InterpreterImpl* interpreter); + + + + +#ifndef FEATS_ON_CMD + void registerMicrostepper(MicroStepImpl* microStepper); + bool hasMicroStepper(const std::string& name); + std::shared_ptr createMicroStepper(const std::string& name, MicroStepCallbacks* callbacks); +#endif + + std::map getIOProcessors(); void listComponents(); @@ -83,6 +95,9 @@ protected: std::map _invokerAliases; std::map, ExecutableContentImpl*> _executableContent; +#ifndef FEATS_ON_CMD + std::map _microSteppers; +#endif #ifdef BUILD_AS_PLUGINS pluma::Pluma pluma; @@ -99,7 +114,6 @@ protected: }; - } #endif /* end of include guard: FACTORY_H_5WKLGPRB */ diff --git a/src/uscxml/plugins/InvokerImpl.h b/src/uscxml/plugins/InvokerImpl.h index 7af2028..71a8e7d 100644 --- a/src/uscxml/plugins/InvokerImpl.h +++ b/src/uscxml/plugins/InvokerImpl.h @@ -21,7 +21,7 @@ #define INVOKERIMPL_H_8A15A102 -#include "uscxml/config.h" +//#include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/plugins/EventHandler.h" #include "uscxml/messages/Event.h" diff --git a/test/benchmarks/LCCA.16.scxml b/test/benchmarks/LCCA.16.scxml new file mode 100644 index 0000000..7943099 --- /dev/null +++ b/test/benchmarks/LCCA.16.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/LCCA.256.scxml b/test/benchmarks/LCCA.256.scxml new file mode 100644 index 0000000..1efb3b8 --- /dev/null +++ b/test/benchmarks/LCCA.256.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/LCCA.4.scxml b/test/benchmarks/LCCA.4.scxml new file mode 100644 index 0000000..14954c1 --- /dev/null +++ b/test/benchmarks/LCCA.4.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/LCCA.512.scxml b/test/benchmarks/LCCA.512.scxml new file mode 100644 index 0000000..a6c0b44 --- /dev/null +++ b/test/benchmarks/LCCA.512.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/LCCA.64.scxml b/test/benchmarks/LCCA.64.scxml new file mode 100644 index 0000000..c134cf5 --- /dev/null +++ b/test/benchmarks/LCCA.64.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/Transitions.16.scxml b/test/benchmarks/Transitions.16.scxml new file mode 100644 index 0000000..4e65c16 --- /dev/null +++ b/test/benchmarks/Transitions.16.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/Transitions.256.scxml b/test/benchmarks/Transitions.256.scxml new file mode 100644 index 0000000..caf059a --- /dev/null +++ b/test/benchmarks/Transitions.256.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/Transitions.4.scxml b/test/benchmarks/Transitions.4.scxml new file mode 100644 index 0000000..46d7dd8 --- /dev/null +++ b/test/benchmarks/Transitions.4.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/Transitions.512.scxml b/test/benchmarks/Transitions.512.scxml new file mode 100644 index 0000000..29b11fc --- /dev/null +++ b/test/benchmarks/Transitions.512.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/Transitions.64.scxml b/test/benchmarks/Transitions.64.scxml new file mode 100644 index 0000000..f08b747 --- /dev/null +++ b/test/benchmarks/Transitions.64.scxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/benchmarks/createBenchmarks.pl b/test/benchmarks/createBenchmarks.pl index cd5cef7..3c02dc3 100755 --- a/test/benchmarks/createBenchmarks.pl +++ b/test/benchmarks/createBenchmarks.pl @@ -9,56 +9,43 @@ no warnings 'recursion'; use Getopt::Long qw(GetOptions); use Data::Dumper; +my $size = 20; 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; +### Find LCCA =========== -sub createFindLCCABenchmark { +sub benchLCCA { my $where = shift; - my $nestingDepth = 20; - my $parallelStates = 20; + my $nestingDepth = $size; + my $parallelStates = $size; - $$where->{'name'} = 'findLCCA'; + $$where->{'name'} = 'benchmark'; $$where->{'type'} = 'scxml'; $$where->{'datamodel'} = 'null'; - $$where->{'intial'} = ""; - for (my $i = 1; $i <= $parallelStates; $i++) { - $$where->{'initial'} .= "id" . ($i*$nestingDepth) . " "; - } + $$where->{'initial'} = "mark"; + # SCION cannot have multiple states in its initial attribute + # for (my $i = 1; $i <= $parallelStates; $i++) { + # $$where->{'initial'} .= "id" . ($i*$nestingDepth) . " "; + # } $$where->{'children'}[0]->{'type'} = 'parallel'; - $$where->{'children'}[0]->{'id'} = "p0"; + $$where->{'children'}[0]->{'id'} = "mark"; for (my $i = 0; $i < $parallelStates; $i++) { - createFindLCCANestedCompounds(\$$where->{'children'}[0]->{'children'}, $nestingDepth, $nestingDepth * $parallelStates + 1); + benchLCCANestedCompounds(\$$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'}; + $$where->{'children'}[1]->{'transitions'}[0]->{'target'} = "mark"; } -sub createFindLCCANestedCompounds { +sub benchLCCANestedCompounds { my $where = shift; my $amount = shift; my $target = shift; @@ -67,7 +54,7 @@ sub createFindLCCANestedCompounds { my $state; $state->{'id'} = "id".$stateId++; $state->{'type'} = "state"; - createFindLCCANestedCompounds(\$state->{'children'}, $amount - 1, $target); + benchLCCANestedCompounds(\$state->{'children'}, $amount - 1, $target); if ($amount == 1) { $state->{'transitions'}[0]->{'target'} = "id".$target; @@ -77,31 +64,32 @@ sub createFindLCCANestedCompounds { } } -sub createFinalParallelBenchmark { +### Final Parallel States =========== + +sub benchFinalParallel { my $where = shift; - my $nestingDepth = 20; - my $finalStates = 20; + my $nestingDepth = $size; + my $finalStates = $size; - $$where->{'name'} = 'finalParallel'; + $$where->{'name'} = 'benchmark'; $$where->{'type'} = 'scxml'; $$where->{'datamodel'} = 'null'; - $$where->{'intial'} = ""; + $$where->{'initial'} = ""; $$where->{'children'}[0]->{'type'} = 'parallel'; - $$where->{'children'}[0]->{'id'} = "p0"; + $$where->{'children'}[0]->{'id'} = "mark"; - $$where->{'children'}[0]->{'transitions'}[0]->{'event'} = 'done.state.p0'; - $$where->{'children'}[0]->{'transitions'}[0]->{'target'} = 'p0'; + $$where->{'children'}[0]->{'transitions'}[0]->{'event'} = 'done.state.mark'; + $$where->{'children'}[0]->{'transitions'}[0]->{'target'} = 'mark'; for (my $i = 0; $i < $finalStates; $i++) { - createFinalParallelNestedFinals(\$$where->{'children'}[0]->{'children'}, $nestingDepth); + benchFinalParallelNested(\$$where->{'children'}[0]->{'children'}, $nestingDepth); } - } -sub createFinalParallelNestedFinals { +sub benchFinalParallelNested { my $where = shift; my $amount = shift; @@ -114,13 +102,60 @@ sub createFinalParallelNestedFinals { } $state->{'id'} = "id".$stateId++; - createFinalParallelNestedFinals(\$state->{'children'}, $amount - 1); + benchFinalParallelNested(\$state->{'children'}, $amount - 1); push @{$$where}, $state; } } +### Conflicting Transitions =========== + +sub benchConflictTrans { + my $where = shift; + + my $nestingDepth = 1; + my $transitions = $size; + + $$where->{'name'} = 'benchmark'; + $$where->{'type'} = 'scxml'; + $$where->{'datamodel'} = 'null'; + $$where->{'initial'} = ""; + + $$where->{'children'}[0]->{'type'} = 'parallel'; + $$where->{'children'}[0]->{'id'} = "mark"; + for (my $i = 0; $i < $transitions; $i++) { + benchConflictTransNested(\$$where->{'children'}[0]->{'children'}, $nestingDepth); + } + + $$where->{'children'}[1]->{'type'} = 'state'; + $$where->{'children'}[1]->{'id'} = "id".$stateId++; + $$where->{'children'}[1]->{'transitions'}[0]->{'target'} = "mark"; + +} + +sub benchConflictTransNested { + my $where = shift; + my $amount = shift; + + if ($amount > 0) { + my $state; + $state->{'id'} = "id".$stateId++; + $state->{'type'} = "state"; + benchConflictTransNested(\$state->{'children'}, $amount - 1); + + if ($amount == 1) { + for (my $i = 0; $i < $size; $i++) { + $state->{'transitions'}[$i]->{'target'} = "mark"; + } + } + + push @{$$where}, $state; + } + +} + + sub writeState { my $state = shift; my $fh = shift; @@ -171,7 +206,6 @@ sub writeMachine { 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 ' xmlns="http://www.w3.org/2005/07/scxml"'; @@ -187,25 +221,35 @@ sub writeMachine { sub xmllint { my $file = shift; - `mv $file $file.unformatted.xml`; - `xmllint --format $file.unformatted.xml > $file`; - `rm $file.unformatted.xml`; + # `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"); - - - $machine = {}; - $stateId = 1; - createFinalParallelBenchmark(\$machine); - writeMachine($machine, "finalParallel.scxml"); - xmllint("finalParallel.scxml"); - + for my $i (4, 16, 64, 256, 512) { + $size = $i; + + $machine = {}; + $stateId = 1; + benchLCCA(\$machine); + # print Dumper($machine); + writeMachine($machine, "LCCA.${size}.scxml"); + xmllint("benchLCCA${size}.scxml"); + + + # $machine = {}; + # $stateId = 1; + # benchFinalParallel(\$machine); + # writeMachine($machine, "benchFinalParallel${size}.scxml"); + # xmllint("benchFinalParallel${size}.scxml"); + + + $machine = {}; + $stateId = 1; + benchConflictTrans(\$machine); + # print Dumper($machine); + writeMachine($machine, "Transitions.${size}.scxml"); + xmllint("benchConflictTrans${size}.scxml"); + } } diff --git a/test/benchmarks/finalParallel.scxml b/test/benchmarks/finalParallel.scxml deleted file mode 100644 index 193fa1c..0000000 --- a/test/benchmarks/finalParallel.scxml +++ /dev/null @@ -1,786 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/benchmarks/findLCCA.scxml b/test/benchmarks/findLCCA.scxml deleted file mode 100644 index 290a323..0000000 --- a/test/benchmarks/findLCCA.scxml +++ /dev/null @@ -1,828 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/src/test-performance.cpp b/test/src/test-performance.cpp index 13fc141..922b1ca 100644 --- a/test/src/test-performance.cpp +++ b/test/src/test-performance.cpp @@ -1,5 +1,6 @@ #include "uscxml/config.h" #include "uscxml/Interpreter.h" +#include "uscxml/plugins/Factory.h" #include "uscxml/debug/Benchmark.h" #include @@ -8,6 +9,19 @@ using namespace uscxml; using namespace std::chrono; +class InterpreterCallbacks { +public: + InterpreterCallbacks() {} + void beforeEnterState(const std::string& stateName, std::function function) { + _beforeEnterStates.push_back(function); + } + + void operator() { + } + + std::list > _beforeEnterStates; +}; + int main(int argc, char** argv) { if (argc < 2) { std::cout << "Expected filename as first parameter" << std::endl; @@ -16,6 +30,17 @@ int main(int argc, char** argv) { Interpreter interpreter = Interpreter::fromURL(argv[1]); + ActionLanguage al; + al.microStepper = Factory::getInstance()->createMicroStepper("large", (MicroStepCallbacks*)interpreter); + interpreter.setActionLanguage(al); + + InterpreterCallbacks callbacks; + callbacks.beforeEnterState("mark", [](const std::string& sessionId) { + std::cout << "foo"; + }); + + + InterpreterState state; system_clock::time_point start = system_clock::now(); -- cgit v0.12