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

Reply via email to