QtScript in 4.6

Posted by Kent Hansen on November 23, 2009 · 21 comments

As previously described (1, 2), Qt 4.6′s QtScript implementation is based on WebKit’s JavaScript engine, JavaScriptCore (you might’ve also heard it being referred to as “SquirrelFish” or “SquirrelFish Extreme”). This blog is an attempt to highlight the major implications of this “under-the-hood” change.

Looking for bars in all the right places

So, is QtScript any faster in 4.6 than in 4.5? As an indicator, I ran the SunSpider benchmark with both versions. Here are the results obtained on my Linux box (longer bars are better; they show speedup over Qt 4.5, so Qt 4.5 results are all 1): Part 1:

Running SunSpider on QtScript with Qt 4.5 vs 4.6

Part 2:

Running SunSpider on QtScript with Qt 4.5 vs 4.6, part 2

Although these results shouldn’t really be that surprising (boosting performance was one of the key reasons for switching to JavaScriptCore, after all), I can think of two principal ways of interpreting them. One is to say that pre-4.6 QtScript is dog slow, whereas in 4.6 it has good performance (AKA “It’s cat fast”). The other interpretation (my personal favorite) is that pre-4.6, QtScript is sufficient for “scripting in the small/medium”, but with 4.6 it gains the ability to better tackle “scripting in the large”. It’s possible to do more heavy processing on the script side, which effectively means that more use cases for application scripting can be supported.

It’s not always going to be faster.

While 4.6 is practically guaranteed to perform better than previous Qt releases for long-running scripts, that’s not necessarily the case for small, short-running scripts, however. JavaScriptCore’s virtual machine is more complex than the old QtScript VM (it’s register-based rather than stack-based, for example) and currently there’s no hotspotting, i.e. scripts are always JIT-compiled (on the platforms where JIT is supported & enabled). This means that for small scripts, it can take more time to compile the script than to actually execute the compiled form, and the old and relatively simplistic QtScript VM could do it faster. For “1 + 2″, the old VM wins.

In order to mitigate this potential issue in the case where you have a small script that you want to run several times, Qt 4.6 comes with a new class called QScriptProgram. QScriptProgram automatically caches the compiled form of your script. This class was added rather late in the 4.6 development and is marked as internal in the 4.6.0 release, so it won’t show up in the documentation, but it’s there and we think it’s safe to start using it… Here’s how:

QScriptEngine engine;
QScriptProgram program("1 + 2");
qDebug() << engine.evaluate(program).toNumber(); // QtScript lazily compiles and executes the program

...

qDebug() << engine.evaluate(program).toNumber(); // QtScript directly executes the compiled program

We are of course interested in hearing what kind of performance experiences you have with QtScript in 4.6, good or bad.

QtScript, JavaScript… What language is it anyway?

The QtScript language is based on the ECMA-262 standard, which is essentially a subset of JavaScript that was standardized after JavaScript was designed. Prior to Qt 4.6, there were two major issues related to QtScript and ECMA-262 compabilitity. First, QtScript wasn’t 100% compliant (yes, that’s the nuance between “based on” and “strictly adheres to” kicking in), so you as a QtScript script author had to learn and keep in mind where it deviated from the standard.

Second, being 100% compliant to the ECMA-262 standard is not really something to strive for anyway, since it’s not the language people use; JavaScript is the “de-facto” ECMAScript variant that’s actually in use out there. Ideally, then, the QtScript language should be JavaScript. It certainly shouldn’t be something that isn’t quite standards-compliant yet doesn’t quite support real-world JavaScript either, because that severely limits its scope and ease of use.

In addition to the “QtScript is JavaScript, so I only need to learn JavaScript”-argument, turning the QtScript language into “proper” JavaScript has two major benefits. First, there’s a lot of existing and yet-to-be-written JavaScript code that doesn’t strictly require a browser to be useful (libraries for doing “class-based” programming, for example). It would be nice if you could just evaluate those scripts with QtScript and be reasonably confident that they won’t blow up. In 4.6, you can. Second, Qt has this other component called QtWebKit that also does JavaScript. It would be nice if QtScript spoke the exact same language. In 4.6, it does. It’s a good start for a tighter integration between those two components. (Unfortunately, in 4.6 that integration is still not present in terms of actually mixing QtWebKit and QtScript C++ APIs.)

“I used to have behavioral issues, now I’m feeling better”

Prior to 4.6, perhaps the place where QtScript did worst in regard to compliance was how it dealt with Date and RegExp objects. Instead of implementing the semantics defined in ECMA-262, Date and RegExp were essentially wrappers around QDateTime and QRegExp. While this was convenient for us to implement, it meant that you could get some “interesting” behavior compared to that of a more compliant engine. Imagine loading an existing JavaScript library, hundreds or maybe thousands of lines in size, where one of the functions defines this finely tuned regular expression that doesn’t quite do what it should in QtScript because QRegExp semantics are not quite the same as those of JavaScript regular expressions… Fun. Actually, that’s a story from real life. Not so fun.

With 4.6 we get well-behaved Date and RegExp objects “for free” from JavaScriptCore. There are lots of other compliance issues and bugs that are gone too.

Of course, it can’t be all roses and peaches. For example, if you’ve been relying on evaluating nameless function expressions as statements, you should be aware of this quirk that will be present in 4.6.0.

License change

As of Qt 4.6, due to the dependency on JavaScriptCore, the QtScript module is only available under the LGPL or a compatible license. If you want to use QtScript under the commercial license, see one more section down.

Changes in platform support

Platform-wise, JavaScriptCore is a very interesting beast compared to the old “bread-and-butter” QtScript implementation. As you may know, QtWebKit is disabled on some platforms because it plain doesn’t build, or it’s not tested in WebKit upstream, so it could break at any time. Since QtScript depends on JavaScriptCore in 4.6, a subset of WebKit needs to be compiled in order to build QtScript. Unfortunately, that subset is quite needy and picky with regard to the tool chain. Consequently, in 4.6 we don’t guarantee that QtScript-with-JavaScriptCore works on Tier 2 platforms where QtScript of previous Qt releases did. Yeah, it sucks, but we don’t have the resources currently to maintain and test all those platforms and configurations. There haven’t been any reports on this matter for the beta and Release Candidate, but in any case there’s a backup solution:

Introducing QtScript Classic

Can’t live with QtScript gone LGPL? QtScript doesn’t build on your platform? Found an (intentional or unintentional) change in QtScript behavior that affects your application when running against Qt 4.6.0? First of all, if your answer is “Yes” to either of the last two questions, we would greatly appreciate it if you report the problem. Second, we are providing the old QtScript back-end as a separate Qt Solution, called QtScript Classic (follow the link for more information and downloads). QtScript Classic can effectively be used to replace the QtScript from 4.6 with the latest from the 4.5 line. Note that Qt 4.6 can be configured with -no-script to avoid building the standard QtScript module. Again, for Tier 2 platforms, if you experience issues with the 4.6.0 release consider trying QtScript Classic.

Conclusion

In Qt 4.6, QtScript should be faster for computation-heavy scripting. It should be more standards-compliant. The C++ API should still behave the same, unless documented otherwise. In short, existing code should mostly continue to work, only faster than before. Please let us know if that’s not the case. Reported issues will appear on the QtScript component page in the bug tracker.

QShare(this)

Possibly related posts:

  1. Faking a web browser environment in QtScript
  2. Say Hello to Envjs for QtScript

21 comments

1 lulzfish November 23, 2009 at 7:54 pm
 

I like how the bars are to scale, so that I can easily make a visual comparison between the speed of 4.5 and 4.6 without having to think about how much a “40%” speedup is.

No, wait, you cut off the 4.5 bars… this is very hard to read.

2 sfa November 23, 2009 at 8:25 pm
 

i like c#. i’m thinking of using mono instead.

3 Florian Link November 23, 2009 at 8:34 pm
 

This is really good news. I had a glance at the code and was wondering if you could add more of the QtScript binding to the QtWebKit binding, e.g. QWebFrame still only has an evaluateJavaScript(QString), while it would be much better to have
QScriptValue QWebFrame::callJavaScriptFunction(QString function, QList args).
And on the WebKit JavaScript inside of the HTML, it would be nicer as well to have the complete QtScript binding available,
currently the binding is quite inferior to what QtScript can do (e.g. handle pointers that are not QObject*).
Having QtScript-like binding from the HTML/JavaScript sources would allow to script Qt from the HMTL pages, using the QtScript wrapper generator.

4 Henrik Hartz November 23, 2009 at 9:20 pm
 

Nice post Kent!! For those interested in getting their FLOP’s worth with JavaScript, here’s an article that describes how JIT-optimized can be made even faster by following guidelines; http://www.cs.tut.fi/%7Esplst09/material/Presentations/Akos.guidelines.pdf

5 Marcus November 23, 2009 at 9:58 pm
 

Hi,

The bars look impressive at least. I was wondering how this effects qml performance as I’d guess the bindings are relatively small pieces of code executed often. ( Of course I know qml isn’t near finished, just wondering ).

Cheers,
Marcus

6 Philippe November 24, 2009 at 1:46 am
 

If QtScript can’t be used with the commercial license, why is it included in the commercial packages?

7 Dmytro November 24, 2009 at 2:01 am
 

Philippe: It can be used, but QtScript part will use LGPL license,
so it’s not allowed to make changes to QtScript without publishing them.

8 QPlace November 24, 2009 at 2:22 am
 

Every product that uses QtScript faces same issues with having to support script editing and debugging. “Reinventing the wheel” by hacking script editor window and attaching debugger to it is something that everyone involved with supporting scripting in his/her app has to deal with.

Are there any plans to ship Qt4.x or Qt5.x with some sort of script editing control with “pre-attached” debugger? It would be so very helpful…

9 daminetreg November 24, 2009 at 9:59 am
 

It sounds very cool ^^

I’ll try the Mootools Server Side version with it to see the result and performance. It could be used for plugin development or for modding in games.

It could also be used too to make a Web Server based on Javascript, and it could be a good alternative to complete web development with C++: Writing classes in C++ in order to get the best performances and using them in Javascript to link them with views and outputs in XHTML in order to get flexibility and fast web sites edition (without recompiling). We will just have to implement a thread to handle http requests.

10 Thorbjørn Lindeijer November 24, 2009 at 11:31 am
 

@lulzfish: The 4.5 bars are not cut off, they are at 1 because what the graph displays is the speed improvement over Qt 4.5. So for example the md5 benchmark runs 20 times faster in Qt 4.6 than it did in Qt 4.5.

11 Henrik Hartz November 24, 2009 at 1:55 pm
 

@Marcus it does affect QML, some specific optimizations have been done with QML in mind without going into detail

12 logarithmic November 24, 2009 at 2:05 pm
 

For such huge speedups you need a logarithmic scale to see the differences between the single tests!

13 Scorp1us November 24, 2009 at 5:10 pm
 

I will not ever use QtScript until I can define new QGraphicsItems in it. It seems that you cannot. If you can, please provide example code, as I have tried and never got anything to work. I realize I can define them in C++ and export them to the script engine, but that’s not really accpetible. Until then I’ll be using PyQt, at least until PySide is stable, where I can define new QGraphicsItems in scripted code.

I am happy though that once (if ever) the class inheritance roperly works, that these speedups will be there to my advantage.

14 Donald Carr November 24, 2009 at 7:10 pm
 

I agree the order of magnitude of improvement in performance requires logarithmic graphs for comfort, but a linear scale gives a toasty warm glow like no other

15 rdale November 24, 2009 at 7:58 pm
 

@Scorp1us: As far a I know you can use QGraphicsItems with both Kent’s Qt Labs QtScript bindings, and also the QtScript bindings based on the ‘Smoke’ libraries. They both have the colliding mice example, which works fine. Do you have a specific bug that is giving you problems?

16 Scorp1us November 25, 2009 at 12:47 am
 

Well, I meant QGraphicsItem derived classes.

Where in python I can do:
class AffineText(QGraphicsItem):
…. def init(self, text, parent):
…. …. QGraphicsItem.__init(self, parent)
…. …. self.path = QGraphicsPathItem()
…. …. self.path.addText(text, …)

…. def paint(self, painter, …)
…. …. painter.drawPath(self.path)

…. def boundingRect(self)
…. …. return self.path.boundingRect()

There is no way to do that in Javascript. I would assume it would look something like:

function AffineText(text) {
this.baseClass = QGraphicsItem;
this.baseClass();
this.path=QPainterPath();
this.path.addText(text);

return this;
}

item.prototype.paint = function (painter, …) {
this.painter.drawPath(this.path);
}
item.prototype.boundingRect = function() {
return this.path.boundingRect();
}

But no matter what I tried I could not get it to work. It seems that QtScript is unable to provide the class and functions to the C++ side. It seems that the new QGraphicsItems’ “attached properties” could be used here…

Thanks for asking. :-)

17 Kent November 26, 2009 at 11:15 am
 

Scorp1us: The “Colliding Mice” example (examples/CollidingMice.qs in the qtscriptgenerator repo) shows how to define a custom QGraphicsItem. The Mouse item type “reimplements” QGraphicsItem::paint() on the JavaScript side.

18 Frank Mertens November 27, 2009 at 12:37 am
 

I would love to have an option to select the backend. I have two use-cases in my app where I’m using QtScript. For one it’s going to be much slower with squirrelfish and for on it’s going to rock. Anyway without having that choice squirrelfish comforts best as a default.

19 Scorp1us December 1, 2009 at 5:51 pm
 

Thanks Kent, will check it out!

20 Jeremiah December 2, 2009 at 6:54 pm
 

Those graphs are pretty lame. If the bars for 4.5 are always 1, why even bother? They’re just taking up space.

21 ShawnDream December 2, 2009 at 10:00 pm
 

Eh – it’s space such a small graph can well afford as the whole point of it is “lookit these HUGE speedups” something that a log graph or a graph without comparison bars would just not do well.

Comments on this entry are closed.

Previous post:

Next post: