Hi all,
Following the recent enhancements of QGsExpression, I was wondering whether it
wouldn't make sense to integrate a language intepreter instead of creating
another language.
One candidate would be Google's V8 JavaScript engine
(http://code.google.com/p/v8/). Since embedding this C++ library is easy, I've
conducted a few quick benchmarks against QgsExpression. Code is attached.
Test 1:
Qgs: '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9' || '0'
total_avg: 3.380211
V8: '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' + '0'
total_avg: 0.556035
Fact: 6.0791335
Test 2:
Qgs: 1+1=2 AND 5>1
total_avg: 4.072254
V8: 1+1==2 && 5>4
total_avg: 0.864054
Fact: 4.7129624
Test 3:
Qgs: replace(lower( 'AAAAAAAAAAxxCCCCCCCC'), 'xx', 'BB')
total_avg: 3.480217
V8: 'AAAAAAAAAAxxCCCCCCCC'.toLowerCase().replace('xx', 'BB')
total_avg: 0.856053
Fact: 4.0654224
Test 4:
Qgs: regexp_replace( 'AAAAAAAAAAxxCCCCCCCC', 'x+', 'b')
total_avg: 2.952184
V8: 'AAAAAAAAAAxxCCCCCCCC'.replace(/x+/, 'b')
total_avg: 0.668042
Fact: 4.4191593
Test 5:
Qgs: CASE WHEN (15 = 11 or 15 = 13 or 15 = 15 or 15 = 21) THEN 15 END
total_avg: 1.4664916
V8: if ([11,13,15,21].indexOf(15)>=0) { 15 }
total_avg: 0.2672168
Fact: 5.4880217
This benchmarks show that QgsExpression evaluation is between 4 to 6 times
slower than V8. This is much better than I expected, but it's still factor 4
to 6...
So I think it's worth discussing pros and cons of using V8 as an expression
engine. I see the following points:
Pros:
-Better performance
-Full Javascript language set included
-Possibility for writing custum functions
Cons:
-New language for expressions
-More fat (3.7MB for libv8.so)
V8 would be a new runtime dependency but would replace the flex and yacc build
dependencies.
Any other points?
Regards
Pirmin
--
Pirmin Kalberer
Sourcepole - Linux & Open Source Solutions
http://www.sourcepole.com>From 9d75ebf5c8f09de66b580f3b14ed9becf627f85b Mon Sep 17 00:00:00 2001
From: Pirmin Kalberer <[email protected]>
Date: Tue, 10 Jan 2012 00:55:08 +0100
Subject: [PATCH] V8 benchmark
---
CMakeLists.txt | 4 ++
cmake/FindV8.cmake | 46 ++++++++++++++++++++++++++++++
tests/bench/CMakeLists.txt | 3 +-
tests/bench/main.cpp | 23 ++++++++++++++-
tests/bench/qgsbench.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++
tests/bench/qgsbench.h | 6 ++++
6 files changed, 147 insertions(+), 2 deletions(-)
create mode 100644 cmake/FindV8.cmake
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 67b686b..e82f373 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,6 +117,10 @@ IF (NOT BISON_EXECUTABLE)
ENDIF (NOT BISON_EXECUTABLE)
#############################################################
+
+FIND_PACKAGE(V8)
+
+#############################################################
# search for dependencies
IF(NOT WIN32 AND NOT ANDROID)
diff --git a/cmake/FindV8.cmake b/cmake/FindV8.cmake
new file mode 100644
index 0000000..e8038ba
--- /dev/null
+++ b/cmake/FindV8.cmake
@@ -0,0 +1,46 @@
+# Locate V8
+# This module defines
+# V8_LIBRARY
+# V8_FOUND, if false, do not try to link to V8
+# V8_INCLUDE_DIR, where to find the headers
+
+FIND_PATH(V8_INCLUDE_DIR v8.h
+ ${V8_DIR}/include
+ $ENV{V8_DIR}/include
+ $ENV{V8_DIR}
+ ~/Library/Frameworks
+ /Library/Frameworks
+ /usr/local/include
+ /usr/include
+ /sw/include # Fink
+ /opt/local/include # DarwinPorts
+ /opt/csw/include # Blastwave
+ /opt/include
+ /usr/freeware/include
+ /devel
+)
+
+FIND_LIBRARY(V8_LIBRARY
+ NAMES v8 libv8
+ PATHS
+ ${V8_DIR}
+ ${V8_DIR}/lib
+ $ENV{V8_DIR}
+ $ENV{V8_DIR}/lib
+ ~/Library/Frameworks
+ /Library/Frameworks
+ /usr/local/lib
+ /usr/lib
+ /sw/lib
+ /opt/local/lib
+ /opt/csw/lib
+ /opt/lib
+ /usr/freeware/lib64
+)
+
+SET(V8_FOUND "NO")
+IF(V8_LIBRARY AND V8_INCLUDE_DIR)
+ SET(V8_FOUND "YES")
+ENDIF(V8_LIBRARY AND V8_INCLUDE_DIR)
+
+
diff --git a/tests/bench/CMakeLists.txt b/tests/bench/CMakeLists.txt
index e505533..70f9db6 100644
--- a/tests/bench/CMakeLists.txt
+++ b/tests/bench/CMakeLists.txt
@@ -22,6 +22,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../../src/core/raster
${CMAKE_CURRENT_BINARY_DIR}
# ${GDAL_INCLUDE_DIR} # remove once raster layer is cleaned up
+ ${V8_INCLUDE_DIR}
)
IF (WITH_INTERNAL_SPATIALITE)
@@ -44,6 +45,7 @@ TARGET_LINK_LIBRARIES(qgis_bench
${QT_QTWEBKIT_LIBRARY}
${QT_QTMAIN_LIBRARY}
${QT_QTTEST_LIBRARY}
+ ${V8_LIBRARY}
)
SET_TARGET_PROPERTIES(qgis_bench PROPERTIES
@@ -61,4 +63,3 @@ INSTALL (TARGETS qgis_bench
IF (APPLE)
INSTALL (CODE "EXECUTE_PROCESS(COMMAND ln -sfh ../../../${QGIS_FW_SUBDIR} \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${QGIS_BIN_DIR}/qgis_bench.app/Contents/Frameworks\")")
ENDIF (APPLE)
-
diff --git a/tests/bench/main.cpp b/tests/bench/main.cpp
index 2f50a6b..fc9ce3b 100644
--- a/tests/bench/main.cpp
+++ b/tests/bench/main.cpp
@@ -531,7 +531,28 @@ int main( int argc, char *argv[] )
}
}
- qbench->render();
+ //qbench->render();
+
+ qbench->start();
+ qbench->expressionTest("'1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9' || '0'");
+ //qbench->expressionTest("1+1=2 AND 5>1");
+ //qbench->expressionTest("replace(lower( 'AAAAAAAAAAxxCCCCCCCC'), 'xx', 'BB')");
+ //qbench->expressionTest("regexp_replace( 'AAAAAAAAAAxxCCCCCCCC', 'x+', 'b')");
+ //qbench->expressionTest("CASE WHEN (15 = 11 or 15 = 13 or 15 = 15 or 15 = 21) THEN 15 END");
+ qbench->elapsed();
+ qbench->calcStats();
+ qbench->printLog();
+ delete qbench;
+
+ qbench = new QgsBench( mySnapshotWidth, mySnapshotHeight, myIterations );
+ qbench->start();
+ qbench->v8test("'1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' + '0'");
+ //qbench->v8test("1+1==2 && 5>4");
+ //qbench->v8test("'AAAAAAAAAAxxCCCCCCCC'.toLowerCase().replace('xx', 'BB')");
+ //qbench->v8test("'AAAAAAAAAAxxCCCCCCCC'.replace(/x+/, 'b')");
+ //qbench->v8test("if ([11,13,15,21].indexOf(15)>=0) { 15 }");
+ qbench->elapsed();
+ qbench->calcStats();
if ( mySnapshotFileName != "" )
{
diff --git a/tests/bench/qgsbench.cpp b/tests/bench/qgsbench.cpp
index d22e538..f9e75b4 100644
--- a/tests/bench/qgsbench.cpp
+++ b/tests/bench/qgsbench.cpp
@@ -25,6 +25,7 @@
#endif
#include <time.h>
#include <math.h>
+#include <v8.h>
#include <QFile>
#include <QFileInfo>
@@ -38,6 +39,7 @@
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
#include "qgsproject.h"
+#include "qgsexpression.h"
#ifdef Q_OS_WIN
// slightly adapted from http://anoncvs.postgresql.org/cvsweb.cgi/pgsql/src/port/getrusage.c?rev=1.18;content-type=text%2Fplain
@@ -112,6 +114,66 @@ int getrusage( int who, struct rusage * rusage )
}
#endif
+void QgsBench::v8test(QString expression) //"'Hello' || ', World!'"
+{
+ using namespace v8;
+
+ // Create a stack-allocated handle scope.
+ HandleScope handle_scope;
+
+ // Create a new context.
+ Persistent<Context> context = Context::New();
+
+ // Enter the created context for compiling and
+ // running the hello world script.
+ Context::Scope context_scope(context);
+
+ // Create a string containing the JavaScript source code.
+ Handle<String> source = String::New(expression.toAscii());
+
+ // Compile the source code.
+ Handle<Script> script = Script::Compile(source);
+
+ // Run the script to get the result.
+ Handle<Value> result;
+ for ( int i = 0; i < mIterations; i++ )
+ {
+ result = script->Run();
+ }
+
+ // Dispose the persistent context.
+ context.Dispose();
+
+ // Convert the result to an ASCII string and print it.
+ String::AsciiValue ascii(result);
+ std::cout << *ascii << std::endl;
+}
+
+void QgsBench::expressionTest(QString expression) //"'Hello' + ', World!'"
+{
+ QgsExpression exp(expression);
+ if (exp.hasParserError())
+ {
+ // show error message
+ std::cout << exp.parserErrorString().toStdString() << std::endl;
+ return;
+ }
+ QVariant result;
+ for ( int i = 0; i < mIterations; i++ )
+ {
+ result = exp.evaluate();
+ }
+ if (exp.hasEvalError())
+ {
+ // show error message
+ std::cout << exp.evalErrorString().toStdString() << std::endl;
+ }
+ else
+ {
+ std::cout << result.toString().toStdString() << std::endl;
+ }
+}
+
QgsBench::QgsBench( int theWidth, int theHeight, int theIterations )
: QObject(), mWidth( theWidth ), mHeight( theHeight ), mIterations( theIterations ), mSetExtent( false )
{
@@ -207,6 +269,11 @@ void QgsBench::render()
elapsed();
}
+ calcStats();
+}
+
+void QgsBench::calcStats()
+{
mLogMap.insert( "iterations", mTimes.size() );
// Calc stats: user, sys, total
diff --git a/tests/bench/qgsbench.h b/tests/bench/qgsbench.h
index e3df4c6..9444c2b 100644
--- a/tests/bench/qgsbench.h
+++ b/tests/bench/qgsbench.h
@@ -43,6 +43,12 @@ class QgsBench : public QObject
void render();
+ void v8test( QString expression );
+
+ void expressionTest( QString expression );
+
+ void calcStats();
+
void printLog();
bool openProject( const QString & fileName );
--
1.7.5.4
_______________________________________________
Qgis-developer mailing list
[email protected]
http://lists.osgeo.org/mailman/listinfo/qgis-developer