Hi guys,
we have fully investigated the example provided by Caio, and want to share
the excellent news with you. As we said before, the "with + function"
construct in JavaScript creates static bindings, which means there is no
dynamic lookup after the function is compiled! This is proved by callgrind
as well.
Peter created a simple benchmark which uses v8 from qtscript-staging. The
benchmark is based on the pattern provided by Caio before, and tests
several different alternatives:
* Normal JS variables / accessor (callback) functions
* With statement / push context API
* Crankshaft is enabled or not
The benchmark itself is attached to this mail.
With crankshaft:
JS accessors: 19.8 s
JS variables: 19.8 s
API accessors: 19.8 s
API variables: 18.4 s
Without crankshaft:
JS accessors: 19.8 s
JS variables: 19.8 s
API accessors: 19.7 s
API variables: 18.4 s
As you can see there is no difference (the numbers varies a little
depending the execution order of the tests, but still, the difference is
negligible).
As for cunclusion I would suggest the with statement, since if you happens
to change the JS engine again, it will still work.
I think this is really a good news, since we don't need to worry about the
lookup speed, since there are no lookups here.
Regards,
Zoltan
> Hello Peter,
>
>> 4. We can reach better performance with "with" if we use function inside
>> "with":
>> with(o) (function() { for(var i = 0; i < 1000000; ++i) Math.sin(i); })()
>> time: 221msec
>>
>> The fourth example just a suggestion to using "with" statement and reach
>> better performace without modify the JS engine.
>> Would it be possible to use the latest form for your purposes?
>
> We might be confusing things here. I'll give a sketch of how the
> bindings are created and evaluated, imagine this snippet of QML
>
> MyItem {
> prop: valueA + valueB + Math.sin(0)
> }
>
> what the engine currently does is (you can checkout
> src/declarative/qdeclarativeexpression.cpp for more detais)
>
> 1) push a "clean" context (which is a function context in JS terms),
> pushContext() from qtscript API
> 2) create an object that represents the context of the expression,
> this object is from a special scriptclass that will perform lookup in
> the properties of the object and other things QML provide.
> 3) create an object that mimics the global object (has Math, etc).
>
> Then both objects (2) and (3) are pushed as scopes (which are with
> contexts in JS terms), the original expression is rewritten as a
> function:
>
> (function() { valueA + valueB + Math.sin(0) })
>
> and evaluated, the value is stored and the clean context is popped.
>
> An _approximation_ in JS of what we do would be:
>
> (function() { // this is the "clean" context
> var context = Engine.createDeclarativeContextForObject(obj);
> var newGlobal = Engine.getStaticGlobalObject();
>
> with (context) {
> with (newGlobal) {
> return (function() { valueA + valueB + Math.sin(0) });
> }
> }
> })(); // we evaluate, store the value and close the "clean" context
>
> The returned value is a function, when executed will give us the value
> of the expression. QML re-executes it when it knows some dependency
> changed.
>
> So this is the way that works right now. We get the repeated execution
> of the "whole package". Are there ways to improve things in this
> scenario?
>
> Our static global object is "static" (has a fixed set of properties),
> could that information do improve lookup speed when inside with()
> contexts?
>
>
> Last month I posted to the list that there's an edge case in Web that
> a similar dynamic behavior is necessary, and they implement it in a
> very similar fashion but without C++ APIs. Maybe this can give some
> insight, or help understanding our case better.
>
> http://thread.gmane.org/gmane.comp.lang.javascript.v8.general/3738/focus=3743
>
> the actual code / comments are in this file:
>
> http://trac.webkit.org/browser/trunk/Source/WebCore/bindings/v8/V8LazyEventListener.cpp
>
>
> Cheers,
>
> --
> Caio Marcelo de Oliveira Filho
> OpenBossa - INdT
> _______________________________________________
> Qt-script mailing list
> [email protected]
> http://lists.qt.nokia.com/mailman/listinfo/qt-script
>#include <v8.h>
#include <sys/time.h>
using namespace v8;
static inline Local<v8::Value> CompileRun(const char* source) {
return Script::Compile(v8::String::New(source))->Run();
}
#if 0
const char* ToCString(const v8::String::Utf8Value& value)
{
return *value ? *value : "<string conversion failed>";
}
Handle<Value> Print(const v8::Arguments& args)
{
bool first = true;
for (int i = 0; i < args.Length(); i++) {
v8::HandleScope handle_scope;
if (first) {
first = false;
} else {
printf(" ");
}
v8::String::Utf8Value str(args[i]);
const char* cstr = ToCString(str);
printf("%s", cstr);
}
printf("\n");
fflush(stdout);
return v8::Undefined();
}
#endif
Handle<Value> getter(Local<String> property,
const AccessorInfo& info)
{
static int x = 0;
return Integer::New(x++);
}
void setter(Local<String> property, Local<Value> value,
const AccessorInfo& info)
{}
void measure(Local<Function> fun, const char* info)
{
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < 100000; ++i)
fun->Call(fun, 0, NULL);
gettimeofday(&end, NULL);
double tS = start.tv_sec*1000000 + start.tv_usec;
double tE = end.tv_sec*1000000 + end.tv_usec;
printf("%s %.0f\n", info, tE - tS);
}
void benchAPI(bool withAccessor = false)
{
HandleScope handleScope;
// Create global context
Handle<ObjectTemplate> global = ObjectTemplate::New();
Persistent<Context> context = Context::New(NULL, global);
context->Enter();
// Create a custom object
Local<ObjectTemplate> instanceTemplate = ObjectTemplate::New();
Local<Object> instance = instanceTemplate->NewInstance();
if (withAccessor) {
instance->SetAccessor(String::New("valueA"), getter, setter);
instance->SetAccessor(String::New("valueB"), getter, setter);
} else {
instance->Set(String::New("valueA"), Number::New(3));
instance->Set(String::New("valueB"), Number::New(4));
}
// Push a clean context
Local<Context> scriptContext = Context::NewFunctionContext();
scriptContext->Enter();
// Push the custom object's scope
Local<Context> instanceContext = scriptContext->NewScopeContext(instance);
instanceContext->Enter();
// Push the global object's scope
Local<Context> globalContext = scriptContext->NewScopeContext(context->Global());
globalContext->Enter();
// Evaluate an expression
Local<Script> code = Script::CompileEval(String::New("(function() { valueA + valueB + Math.sin(0) });"));
Local<Value> result = code->Run();
globalContext->Exit();
instanceContext->Exit();
// Close the clean context
scriptContext->Exit();
Local<Function> fun = Local<Function>::Cast(result);
measure(fun, withAccessor ? "API accessor:" : "API: ");
// Close the global context
context->Exit();
context.Dispose();
}
void benchJS(bool withAccessor = false)
{
HandleScope handleScope;
// Create global context
Handle<ObjectTemplate> global = ObjectTemplate::New();
Persistent<Context> context = Context::New(NULL, global);
Context::Scope contextScope(context);
Local<ObjectTemplate> instanceTemplate = ObjectTemplate::New();
Local<Object> instance = instanceTemplate->NewInstance();
if (withAccessor) {
instance->SetAccessor(String::New("valueA"), getter, setter);
instance->SetAccessor(String::New("valueB"), getter, setter);
} else {
instance->Set(String::New("valueA"), Number::New(3));
instance->Set(String::New("valueB"), Number::New(4));
}
Local<Object> globalObject = context->Global();
globalObject->Set(String::New("context"), instance);
globalObject->Set(String::New("newGlobal"), globalObject);
Local<Value> result = CompileRun(
"(function() {"
" with(context)"
" with(newGlobal)"
" return (function() { valueA + valueB + Math.sin(0) });"
"})();");
Local<Function> fun = Local<Function>::Cast(result);
measure(fun, withAccessor ? "JS accessor: " : "JS: ");
context.Dispose();
}
int main(int argc, char* argv[])
{
benchJS(true);
benchJS();
benchAPI(true);
benchAPI();
return 0;
}
_______________________________________________
Qt-script mailing list
[email protected]
http://lists.qt.nokia.com/mailman/listinfo/qt-script